5

Can anyone explain why this code is giving output as null? When I try to call new A() instead of new B(), it is printing the current date.

class A
{
    Date d = new Date();

    public A()
    {
        printDate();
    }

    void printDate() 
    {
        System.out.println("parent");
        System.out.println(d);
    }
}

class B extends A
{
    Date d = new Date();

    public B()
    {
        super();
    }

    @Override
    void printDate()
    {
        System.out.println("child");
        System.out.println(d);
    }
}

public class Test 
{
    public static void main(String[] args) 
    {
        new B();
    }
}
Jonas
  • 121,568
  • 97
  • 310
  • 388
chiranjeevi
  • 73
  • 2
  • 6

5 Answers5

4

new B() invokes the constructor of B, which invokes the constructor of A. A's constructor calls printDate(), which, due to the overriding, executes B's printDate(), which prints the value of d variable of B. However, d variable of B is not initialized yet (it will only be initialized after the constructor of A is executed). Therefore it is still null (which is the default value for reference variables).

On the other hand, when you create an instance of A (new A()), printDate of A is called, and it prints the d variable of A, which was initialized prior to the constructor of A being executed.

In case it's not clear, B.d does not override A.d, it just hides it. Only methods can be overridden.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • Are you sure? `B` also defines `d` which shadowes `d` from `A` – ooxi Nov 19 '14 at 09:55
  • 1
    `A.d` is not visible from `B.printDate()` – Jens Baitinger Nov 19 '14 at 09:58
  • @ooxi That's correct. This is the reason `printDate()` of B prints the `d` variable of B. – Eran Nov 19 '14 at 09:58
  • yes Eran, I have also same explanation, but my question is when i call new A(), at this time also constructor execution is not yet completed, so the d value should not be available in printDate(). there by it should print null. right.? – chiranjeevi Nov 19 '14 at 10:04
  • @ooxi you are correct. when I say "d" it is trying to get value from A. B has already its own copy of d, so it is not inherited d value from A. – chiranjeevi Nov 19 '14 at 10:07
  • @chiranjeevi in the first case, the init order is : member variables of A (A.d), constructor of A, member variables of B (B.d), constructor of B. In the second case : member variables of A (A.d), constructor of A. Therefore, in the second case A.d is already initialized when the constructor is called. – Eran Nov 19 '14 at 10:08
1

Declare Date as Static

static Date d = new Date();
    public B(){
        super();
    }
    @Override
    void printDate(){
        System.out.println("child");
        System.out.println(d);
    }
SonalPM
  • 1,317
  • 8
  • 17
  • yeh, I know it works if I add static, but I want to know why my code is failing. and when I call new A() why it is not failing. – chiranjeevi Nov 19 '14 at 09:57
1

When you instantiate a B:

  1. the B constructor chains to the A constructor

  2. the A constructor calls printDate, but it is the B.printDate override that is called

  3. the B.printDate prints B.d.

  4. that gives null because the B.d field hasn't been initialized

  5. the printMethod returns, and the A constructor returns

  6. the fields of B are initialized, setting B.d to a non-null value. But its too late to affect the output.


Some lessons to learn from this:

  • It is a bad idea for a constructor to call an instance method on the object it is creating unless that instance method is either private or final. If you manage to call a method that has been overloaded by a subclass, then that method will see the instance variables of the subclass before they have been initialized ... and that's problematic.

  • It is a bad idea to use the same name for a (non-private) field in by a class and a subclass. Both fields will exist, and it can be difficult for someone reading the code to keep it right in their head.

  • Non-private instance fields are a bad idea. They are bad for encapsulation.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
1

This is because when you call super() then the Date variable d defined in class A gets initialized. Since in class B the variable d is an object variable, and it overwrites the variable d from parent class, and object variables are constructed only after the this pointer is constructed, you are getting null. If you want that to work as expected, make variable d in class B as static variable, which will then be initialized with class loading.

gashu
  • 484
  • 6
  • 17
1

After all inputs, I hope following answer satisfies,

As B also has field d, A.d is not inherited to B, where B has its own copy of d.

So when the control is in B.printDate(), it tries to look for A.d (because function is called from A).

this works fine if I remove following line from B

Date d = new Date();
chiranjeevi
  • 73
  • 2
  • 6