I'm noticing some strange behavior. I have the following classes:
public abstract class BaseFoo
{
public BaseFoo(String key)
{
Data data = Something.load( key );
load( data );
}
public abstract void load(Data data);
}
public class Foo extends BaseFoo
{
@Expose public long id = 0;
@Expose public String name = "";
//...
public Foo(String key)
{
super(key);
}
@Override
public void load(Data data)
{
this.id = data.id;
this.name = data.name;
//snip setting misc other fields
}
}
Now, if I do the following:
Foo f = new Foo ( "abcd" );
Then I expect f.id
to contain the id of the Foo
record which was loaded. However, its value is actually 0
. By running this code through a debugger, I've found that Foo.load()
is called before the public long id = 0
line is executed. So, although load()
is called and it does set id
and other fields to their correct values, those values are then overwritten by the public long id = 0;
and other variable declarations..
I've never come across this issue before, usually the values set in a constructor overwrite the default values in the variable declaration. Is it because I'm calling load through super
that the values are being overwritten? If so, is there a convenient fix for this?
This is the problem with calling virtual methods in a constructor...
The order of execution is:
That behaviour is well documented in the JLS, section 12.5.
So actually, if you change these:
@Expose public long id = 0;
@Expose public long name = "";
to
@Expose public long id;
@Expose public String name;
and then conditionally set name
to "" if it's not already non-null by the time you get to the Foo
constructor body, then I think you'll be okay.
However, I'd strongly advise you to approach this with a different design. Virtual method calls in constructors get really messy really quickly.
See more on this question at Stackoverflow