views:

141

answers:

7

Heres some sample code,

class Base
{
  private int val;

  Base() {
  val = lookup();
  }

  public int lookup() {
    //Perform some lookup
  // int num = someLookup();
  return 5;
  }

  public int value() {
  return val;
  }
}

class Derived extends Base
{
  private int num = 10;

  public int lookup() {
  return num;
  }
}


class Test
{
  public static void main(String args[]) {

  Derived d = new Derived();
  System.out.println("d.value() returns " + d.value());

  }
}

output: d.value() returns 0 // I expected 10 as lookup() is overridden, but not 0! can someone clarify this?

The initialization of Derived's instance variables has not happened at the time its lookup method executes. How do I make sure the instance variables of Derived are initialized when its method is called?

+3  A: 

The reason you are getting 0 returned is that the constructors Base is being called (and calling lookup in Derived) before 10 is assigned to num in Derived.

To put generally, the base constructor is called before the derived instance fields are initialised.

andrewmu
@andrewmu, that implies a derived class's methods can be run even though its instance variables havent been initialized yet?
Zaki
@Zaki, yes that's right. Which is why, as Andrzej Doyle mentions, it's a bad idea to call a non-final method from constructor. I agree that the behaviour is counter-intuitive, even though it has some consistency (base classes initialised before derived ones).
andrewmu
+1  A: 

You have overriden method lookup() in the Derived class, so when the Base constructor is called it calls the method from Derived which body is return num. At the time of Base initialization the num instance variable of the Derived is not yet initialized and is 0. That's why val is assigned to 0 in Base.

If I understood your intentions correctly, you should change the value method in Base to be:

public int value() {
return lookup();
}
ZloiAdun
+6  A: 

Well for a start, that code doesn't compile due to the lack of someLookup method.

Anyway, asides from that I believe your issue is that your expections are invalid because of the way constructors are run hierarchically.

A superclass' constructor is always run before the subclass', and this includes initializers for the subclass' variables (which are really run as part of the constructor). So, when you create your instance of Derived, the following happens:

  1. The Base constructor is invoked first.
  2. lookup() is called, which uses the implementation in Derived.
  3. num is returned, which is the default value at this point because Derived's constructor and initializers have not been run.
  4. val is set to 0.
  5. The Derived initializers and constructor are run - calling lookup from this point on will return 10.

In general, it's a bad idea to call a non-final method from a constructor for exactly this reason, and many static analysis tools will warn you against it. It's similar to letting object references leak during construction, you can end up with an instance that invalidates class-level invariants (in your case, Derived's num is "always" 10 yet it can be seen to be 0 at some points).

Edit: Note that for this particular case, without any additional code, you could resolve the issue by making num a constant:

class Derived extends Base
{
  private static final int num = 10;
  ...

This would actually do what you want, because the static initializer is run when the class is loaded (which has to happen before the constructors are called). This does however assume that it's fine for:

a) all instances of the class to share the same num variable; b) num never needs to change (if this is true then (a) is true automatically).

In the exact code you've given this is clearly the case, but I expect you may be omitting extra functionality for brevity.

I include this here for comparison and interest, not because it's a workaround to this "issue" in a general sense (because it's not).

Andrzej Doyle
that lookup() is just dummy there, can comment it out.
Zaki
@Andrzej Doyle, Thanks.
Zaki
+2  A: 

The below piece of code is returing 0 (you would expect 10 by looking at the program) when the constructor makes a call to this. The simple reason is that num is not initialized yet and the parent class calls this method.

public int lookup() {
    return num;
}
Bragboy
+1  A: 

It is generally a bad idea to call methods in a constructor that can be overriden in a subclass. In your example the following happens:

  • Derived constructor is called
    • Base constructor is called as its first action
    • Base constructor calls lookup
  • Derived constructor continues and initialies num to 10

Since the subclass constructor is not finished when the base constructor calls lookup, the object is not yet completely initialized and lookup returns the default value of the num field.

Jörn Horstmann
+1, lesson learnt: it is indeed bad practice to call non-final methods from a constructor.
Zaki
+1  A: 

Let's take it slowly:

class Test
{
  public static void main(String args[]) {
  // 1
  Derived d = new Derived();
  // 2
  System.out.println("d.value() returns " + d.value());    
  }
}

Step 1, you call the (default) constructor on Derived, before setting num = 10, it chains up to Base's constructor, which calls Derived's lookup method, but num has not been set, so val remains uninitialized.

Step 2, you call d.value(), which belongs to Base, and val is unset due to 1, and therefore you get 0 instead of 10.

Cambium
+1  A: 

There are a lot of great answers already on why you can't access subclass fields while constructing the base class, but I think you asked for a how: a working solution for something like this:

public abstract class Animal {
  public Animal() {
    System.println(whoAmI());
  }
  public abstract String whoAmI();
}

public Lion() extends Animal {
  private String iAmA = "Lion";
  public Lion(){super();}
  public String whoAmI() {return iAmA;}
}

The practical way is to introduce an init() method on the base class an call it from the subclass's constructor, like:

public abstract class Animal {
  private boolean isInitialized = false;
  public Animal() {}
  void init() {
    isInitialized = true;
    System.out.println(whoAmI());
  }
  public abstract String whoAmI();
  public void someBaseClassMethod() {
    if (!isInitialized)
      throw new RuntimeException("Baseclass has not been initialized");
    // ...
  }
}

public Lion() extends Animal {
  private String iAmA = "Lion";
  public Lion() {
    super();
    init();
  }
  public String whoAmI() {return iAmA;}
}

Only problem is, you can't force subclasses to call the init() method on the base class and the base class might not be properly initialized. But with a flag and some exceptions we can remind the programmer at runtime that he should have called init()...

Andreas_D