Reputation: 3085
I ended up the following scenario in code earlier today (which I admit is kinda weird and I have since refactored). When I ran my unit test I found that a field initialization was not set by the time that the superclass constructor has run. I realized that I do not fully understand the order of constructor / field initialization, so I am posting in the hopes that someone explain to me the order in which these occur.
class Foo extends FooBase {
String foo = "foobar";
@Override
public void setup() {
if (foo == null) {
throw new RuntimeException("foo is null");
}
super.setup();
}
}
class FooBase {
public FooBase() {
setup();
}
public void setup() {
}
}
@Test
public void testFoo() {
new Foo();
}
The abbreviated backtrace from JUnit is as follows, I guess I expected $Foo.<init> to set foo.
$Foo.setup
$FooBase.<init>
$Foo.<init>
.testFoo
Upvotes: 8
Views: 2731
Reputation: 1130
Here's an example of polymorphism in pseudo-C#/Java:
class Animal
{
abstract string MakeNoise ();
}
class Cat : Animal {
string MakeNoise () {
return "Meow";
}
}
class Dog : Animal {
string MakeNoise () {
return "Bark";
}
}
Main () {
Animal animal = Zoo.GetAnimal ();
Console.WriteLine (animal.MakeNoise ());
}
The Main function doesn't know the type of the animal and depends on a particular implementation's behavior of the MakeNoise() method.
class A
{
A(int number)
{
System.out.println("A's" + " "+ number);
}
}
class B
{
A aObject = new A(1);
B(int number)
{
System.out.println("B's" + " "+ number);
}
A aObject2 = new A(2);
}
public class myFirstProject {
public static void main(String[] args) {
B bObj = new B(5);
}
}
out: A's 1 A's 2 B's 5
My rules: 1. Don't initialize with the default values in declaration (null, false, 0, 0.0...). 2. Prefer initialization in declaration if you don't have a constructor parameter that changes the value of the field. 3. If the value of the field changes because of a constructor parameter put the initialization in the constructors. 4. Be consistent in your practice. (the most important rule)
public class Dice
{
private int topFace = 1;
private Random myRand = new Random();
public void Roll()
{
// ......
}
}
or
public class Dice
{
private int topFace;
private Random myRand;
public Dice()
{
topFace = 1;
myRand = new Random();
}
public void Roll()
{
// .....
}
}
Upvotes: 0
Reputation: 1500785
Yes, in Java (unlike C#, for example) field initializers are called after the superclass constructor. Which means that any overridden method calls from the constructor will be called before the field initializers are executed.
The ordering is:
Basically, it's a bad idea to call non-final methods in constructors. If you're going to do so, document it very clearly so that anyone overriding the method knows that the method will be called before the field initializers (or constructor body) are executed.
See JLS section 12.5 for more details.
Upvotes: 10
Reputation: 691755
A constructor's first operation is always the invocation of the superclass constructor. Having no constructor explicitely defined in a class is equivalent to having
public Foo() {
super();
}
The constructor of the base class is thus called before any field of the subclass has been initialized. And your base class does something which should be avoided: call an overridable method.
Since this method is overridden in the subclass, it's invoked on an object that is not fully constructed yet, and thus sees the subclass field as null.
Upvotes: 3