Because it was normally used in a certain Android library.
Suddenly, do you see the output of the following implementation?
public class Main {
static class Foo {
public Foo() {
init();
}
protected void init() {
}
}
static class Bar extends Foo {
private int val1;
private int val2 = 0;
@Override
protected void init() {
val1 = 1;
val2 = 2;
}
}
public static void main(String... args) {
Bar bar = new Bar();
System.out.println(bar.val1);
System.out.println(bar.val2);
}
}
The output result is as follows.
1
0
The Foo class is a constructor that calls the ʻinit ()` method.
The Bar class is a subclass of Foo, overriding the ʻinit ()method created byFoo` to initialize member variables.
In Bar.init (), the member variables val1 and val2 are assigned 1 and 2, respectively.
If you instantiate this Bar class and output each value, val1 contains 1 as you can see, but the value of val2 that should have been assigned 2 is0 It is.
There are two keys to unraveling this phenomenon.
One is the definition of member variables, and the other is the timing of variable initialization in Java.
In the code in question, the member variables of the Bar class are defined as follows:
Bar
private int val1;
private int val2 = 0;
Regarding the definition of member variables, 0 is assigned for primitives and null for classes is assigned if no explicit initialization is performed.
However, even if it is initialized with 0, its behavior changes depending on whether it is explicitly initialized or not.
In Java, the initialization of member variables is done in the constructor. The question is when is it happening in the constructor.
As I usually write casually, the initialization of member variables is actually done ** immediately after calling the constructor of the parent class **.
In other words
Foo
static class Foo {
public Foo() {
← here
init();
}
}
is.
I don't think you can see anything, but the Java constructor requires ** calling the parent class constructor at the beginning of the constructor **. However, there is a condition that can be omitted, which is ** the parent class defines the default constructor **. The default constructor is a constructor that takes no arguments. (Strictly speaking, it doesn't have to be the parent constructor, but I'll omit it.)
So, in the code above, it's the same as having a super () call at the arrow position.
Based on the above, I will explain the trap that I stepped on in the code in question.
It was the Bar class that took the trap.
Bar
static class Bar extends Foo {
private int val1;
private int val2 = 0;
@Override
protected void init() {
val1 = 1;
val2 = 2;
}
}
If you write this class without omitting it, it will be as follows.
Bar
static class Bar extends Foo {
private int val1;
private int val2 = 0;
public Bar() {
super();
}
@Override
protected void init() {
val1 = 1;
val2 = 2;
}
}
The Bar constructor callssuper (), that is, the Foo constructor.
The Foo constructor calls ʻinit (), which is an override of the Bar class, so it's a call to Bar.init () `.
If you expand this flow roughly
Bar() {
super() {
Bar.init() {
Bar.val1 = 1;
Bar.val2 = 2;
}
}
Initialization of Bar member variables() {
Bar.val2 = 0;
}
}
It will be the flow.
Isn't val1 initialized with 0 after super ()? You may think that, if it is not initialized at the time of definition, the initialization process itself after super () will not be performed.
By the way, if you refer to val2 ofBar.init ()before assigning a value, 0 will be returned.
Imagine that all member variables are assigned 0 or null regardless of the initialization of the member definition, and the value specified after super () is assigned.
In the first place, the implementation I posted first is the dung code, so why not use it after understanding inheritance?
(It should have been officially written, but I forgot the link. If I find it, I will paste it ...)
Recommended Posts