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 by
Foo` 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