constructor weirdness - Java bug or as intended?
Notice that the two "dumps" happen one right after another--one as the very last thing the constructor does, and the other one as the first call after the constructor. Yet b goes back to being 0...
-
public class A
{
public int a = 0;
public A()
{
init();
}
public void init()
{
a = 1;
}
}
-
public class B extends A
{
public int b = 0;
public B()
{
super();
}
public void init()
{
super.init();
b = 1;
System.out.println("DUMPING FROM B.INIT:");
dump();
}
public void dump()
{
System.out.println("B:");
System.out.println(a);
System.out.println(b);
}
}
--
...
B myB = new B();
myB.dump();
...
And the output is:
DUMPING FROM B.INIT:
B:
1
1
B:
1
0
I think the issue here is that method overriding always looks for the method lowest in the chain of inheritance first. When init() is called from your parent class, the init() method is available to be called in both the parent and child class and, based on the rules of method overriding, the child's init() method will be called.
Yes, and the println from B::init shows that it is being called, which is why we get the first dump with both values set to 1. The question is why does the value of B.b revert back to 0 after that (second dump)?
This code is confusing to me...how is the second call to dump being made without printing "DUMP FROM blah blah"?
Just below the public int b= 0; line add the following block:{ System.out.println("b= 0"); }
and see what happens. Your super class object assumes the existence
of the subclass object which is still in statu nascendi.
kind regards,
Jos
The "DUMPING FROM ..." is just a println in B::init(), it's not part of dump() itself.
JosAH -- thanks, you're absolutely right! If I declare b as public int b; (without = 0), the second dump prints out two 1s. So I guess the answer is that members get allocated before the constructor (makes sense) but any inline initialization takes place AFTER the constructor is called. Which is still weird. I mean, if I didn't have any inheritance and it was a single class where an integer member was set to 0 inline and then in the constructor was set to 1, I'd expect it to stay 1 when the object is constructed. In this case, it looks like because of the inheritance, the inline init happens after the constructor.
> JosAH -- thanks, you're absolutely right! If I declare b as public int b;
> (without = 0), the second dump prints out two 1s. So I guess the
> answer is that members get allocated before the constructor
> (makes sense) but any inline initialization takes place AFTER the
>constructor is called. Which is still weird.
Yes; the trick is that method overriding is available before the subclass
object has been initialized (very much unlike C++). Java instantiates
objects from the inside out, i.e. when the new operator is called,
the first thing that happens after the ctor is invoked, another ctor is invoked
or a superclass ctor is invoked. After that one returns the initialization
of the object is performed (as in public int b= 0).
When the superclass ctor is in control method overriding is valid already.
That's why you experienced that 'strange' behaviour because a
subclass method was called before that same subclass object was
even 'born' and that subclass's initialization block had a chance to execute.
kind regards,
Jos
> JosAH -- thanks, you're absolutely right! If I
> declare b as public int b; (without = 0), the second
> dump prints out two 1s. So I guess the answer is
> that members get allocated before the constructor
> (makes sense) but any inline initialization takes
> place AFTER the constructor is called. Which is
> still weird. I mean, if I didn't have any
> inheritance and it was a single class where an
> integer member was set to 0 inline and then in the
> constructor was set to 1, I'd expect it to stay 1
> when the object is constructed. In this case, it
> looks like because of the inheritance, the inline
> init happens after the constructor.
The complete sequence is [url http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.5]here[/url].
> JosAH -- thanks, you're absolutely right! If I
> declare b as public int b; (without = 0), the second
> dump prints out two 1s. So I guess the answer is
> that members get allocated before the constructor
> (makes sense) but any inline initialization takes
> place AFTER the constructor is called.
Not quite.
Parent's initializers ("inline initialization")
Parent's constructor
Child's intializers
Child's constructor
With other stuff interleaved as per the link to the JLS in my last post.
> The complete sequence is [url http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.5]here[/url].I like the way you did that--you can actually tell that it's a link the way you did it. :)
> > The complete sequence is [url http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.5]here[/url].
>
> I like the way you did that--you can actually tell
> that it's a link the way you did it. :)
Yeah, unless you take fairly extraordinary measures, it can be hard to spot links in these forums. Lovely bloody colors. :rolleyes:
> The complete sequence is here.It's a lie. That misses out assignment to this$0 (and similar) variables, which occurs before the call to the super-constructor in Java 1.4 and later.
> > The complete sequence is here.
>
> It's a lie. That misses out assignment to this$0 (and
> similar) variables, which occurs before the
> call to the super-constructor in Java 1.4 and later.
Eh? this$0? Is that a secret member variable that seeds this when a method or constructor is entered? Since this$0 doesn't exist as far as the programmer is concerned, I'd say it's irrelevant, but maybe I'm mising something. Care to elaborate?
> > The complete sequence is here.
>
> It's a lie. That misses out assignment to this$0 (and
> similar) variables, which occurs before the
> call to the super-constructor in Java 1.4 and later.
Are you talking about local variables that don't even belong to the object being created and are copied behind the scenes? Is that not an implementation detail, or perhaps an attribute of the memory model rather than object creation.
> Eh? this$0? Is that a secret member variable that seeds
> this when a method or constructor is entered?
It's the link from a non-static inner class to its containing instance. public class Foo
{
public class Bar
{
}
}
generates Foo$Bar.class which javap's to public class Foo$Bar extends java.lang.Object{
final Foo this$0;
public Foo$Bar(Foo);
Code:
0:aload_0
1:aload_1
2:putfield#1; //Field this$0:LFoo;
5:aload_0
6:invokespecial#2; //Method java/lang/Object."<init>":()V
9:return
}
For anyone who can't read bytecode, that means public Foo$Bar(Foo foo)
{
this.this$0=foo;
super();
}
If I recompile with -source 1.3 -target 1.3 then I get public class Foo$Bar extends java.lang.Object{
private final Foo this$0;
public Foo$Bar(Foo);
Code:
0:aload_0
1:invokespecial#1; //Method java/lang/Object."<init>":()V
4:aload_0
5:aload_1
6:putfield#2; //Field this$0:LFoo;
9:return
} instead.
This was changed in 1.4 because it led to a lot of confusing NPEs when the super-constructor called overridden methods which tried to access the enclosing instance of the outer class - particularly an issue for Swing developers, because they tend to use a lot of inner classes.
