The fields I am declaring and immediately instantiating are null
. Here is a sample code:
public class NullFieldSSCCE {
static abstract class Parent {
List<String> values;
Parent() {
values = getValues();
}
protected abstract List<String> getValues();
}
static class Child extends Parent {
String param1="test1";
String param2="test2";
Child() {
}
@Override
protected List<String> getValues() {
return Arrays.asList( new String[] {param1, param2} );
}
}
public static void main(String[] args) {
Child child = new Child();
System.out.println("Child p1="+child.values.get(0)+", p2="+child.values.get(1));
}
}
The result of running this is
Child p1=null, p2=null
while I would expect
Child p1=test1, p2=test2
How is this possible? These fields are instantiated at the same moment the class is, aren't they?
What you have encountered is described in JLS in chapters 8.3.2
and 8.8
. You can find some less detailed and perhaps easier to read information here.
The issue here is the order of initialization of an object.
Here is the order of initialization for your example:
Parent
fields are instantiated: values
becomes an instance of ArrayList
Child
constructor calls super()
, hence invoking Parent
constructorgetValues()
is called; in this case, it is Child.getValues()
;Child
fields are instantiated: param1
and param2
have their values assignedChild
constructor continues - and does nothing more in your caseYou assumend that all fields from all the classes in given hierarchy are instantiated at the same time, that 4
happens before 3
and roughly at the same time as 1
. This assumption is, unfortunately, wrong.
Even if you have read the links I have given you, you may not be certain how things will work in a specific case. Whenever you have doubts about order of initialization do not hesitate to add printf
s to check it. I've done it for you this time:
public class NullFieldSSCCE {
static abstract class Parent {
List<String> values = new ArrayList<String>() {{
System.out.println("Parent.values instantiation");
}};
Parent() {
System.out.println("Parent()");
values.addAll(getValues());
}
protected abstract List<String> getValues();
}
static class Child extends Parent {
String param1="test1";
String param2="test2";
Object param3 = new Object() {{System.out.println("Child.param3 instantiation"); }};
Child() {
System.out.println("Child()");
}
@Override
protected List<String> getValues() {
System.out.println("Child.getValues()");
return Arrays.asList( new String[] {param1, param2} );
}
}
public static void main(String[] args) {
System.out.println("start");
Child child = new Child();
System.out.println("Child p1="+child.values.get(0)+", p2="+child.values.get(1));
}
}
And the output is:
start
Parent.values instantiation
Parent()
Child.getValues()
Child.param3 instantiation
Child()
Child p1=null, p2=null
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With