strange inner class behavior
[CODE]
class Outer {
private String foo = "Outer.foo";
private String bar = "Outer.bar";
private Inner1 inner1 = new Inner1();
private Inner2 inner2 = new Inner2();
private class Inner1 {
private String bar = "Inner1.bar";
Inner1() {
System.out.println("new " + getClass().getName() + ": ");
System.out.println(" foo=" + getFoo());
System.out.println(" bar=" + getBar());
}
String getFoo() {
return foo;
}
String getBar() {
return bar;
}
}
private class Inner2 extends Inner1 {
private String foo = "Inner2.foo";
Inner2() {
}
String getFoo() {
return foo;
}
String getBar() {
return bar;
}
}
public static void main(String[] args) {
new Outer();
}
}
[/CODE]
Output:
new Outer$Inner1:
foo=Outer.foo
bar=Inner1.bar
new Outer$Inner2:
foo=null
java.lang.NullPointerException
at Outer.access$100(JavaTest.java:1)
at Outer$Inner2.getBar(JavaTest.java:37)
at Outer$Inner1.<init>(JavaTest.java:14)
at Outer$Inner2.<init>(JavaTest.java:29)
at Outer.<init>(JavaTest.java:6)
at Outer.main(JavaTest.java:43)
I can't explain the null situation here.
:confused:
why NullPointerExceptin is thrown? why foo is null in Inner2 class?what is the initiating order of a java class ?
[1511 byte] By [
Sun.LiWei] at [2007-9-30 17:21:28]

Your class works as it should. When you are calling the System.out.println functions in the class Inner1, since Inner1 has access to Outer's members, foo is defined. When you are constructing Inner2 the first thing Java does is initialize Inner1 with Inner2's memory map. That means that any variables and functions that are overload in Inner2 will be affected by Inner1's calls. Inner2 overloads variable foo which isn't initialized until later, so its value is null in Inner1. Place those same outputs in the Inner2 class and you will see that foo does indeed end up with a value. The output will be:
// the call to Inner1's constructor
new Outer$Inner2:
foo=null
bar=Inner1.bar //NullPointerException will happen here (see below for explanation)
// the call to Inner2 constructor
new Outer$Inner2:
foo=Inner2.foo
bar=Inner1.bar
Now on to your NullPointerException. Again this is expected behavior. From Inner2 you are trying to access private data from Inner1. The compiler is getting confused between the private data in Outer, which Inner2 has access to, and the private data in Inner1, which isn't accessible. This confusion is why the compiler lets it slip through without error. At runtime this confusion is what leads to the NullPointerException. Remove the function getBar from Inner2. The output is identical but now you aren't trying to do something illegal.
> Your class works as it should. When you are calling
> the System.out.println functions in the class Inner1,
> since Inner1 has access to Outer's members, foo is
> defined. When you are constructing Inner2 the first
> thing Java does is initialize Inner1 with Inner2's
> memory map. That means that any variables and
> functions that are overload in Inner2 will be affected
> by Inner1's calls. Inner2 overloads variable foo
> which isn't initialized until later, so its value is
> null in Inner1.
Good guess but no.
Inner1 ends up with a reference to the Outer instance.
So the code in the Inner1 constructor ends up looking like this....
String getFoo() {
return getOuterInstance().foo;
}
Inner2 on the other hand has a seperate variable called 'foo'.And so getFoo() in Inner2 remains unchanged. (To avoid confusion call this second variable foo2.)
What happens with the call in the constructor of Inner1 is that it calls the overridden method of Inner2. And that returns foo2. And when the constructor for Inner1 runs the initializatoin expression for foo2 has not run yet so it is null.
>
> why NullPointerExceptin is thrown?
As to why it is null see my explaination.
It is thrown because you are trying to use it.
Note also that your example code is designed wrong.
- You are using variable with the same name and expecting them to be related. They are not. There is no way to make them related.
- You are calling an overloaded method in a constructor (do NOT do this.)
> The compiler is
> getting confused between the private data in Outer,
> which Inner2 has access to, and the private data in
> Inner1, which isn't accessible. This confusion is why
> the compiler lets it slip through without error.
The compiler certainly isn't getting confused - its doing its job properly.
foo is null when constructing the in the Inner2 instance because other than fields being initialised to their default values (null for references), no other initialisation of Inner2 takes place until the Inner1 constructor has completed. By calling a polymorphically overridable method from the Inner1 constructor, you are asking for touble - don't do it unless you have very good reason to and have documented your reasoning well.
You get a NullPointerException because the Inner2 constructor is altered by the compiler to take a single argument, which is a reference to its enclosing Outer instance. For the same reasons that Inner2.foo is null, so is this reference to the enclosing instance. When you try to access bar (which is really, as jschell said, something like enclosingInstance.bar, then enclosingInstance (or whatever the compiler chose to call it), is still null, so you get a NUllPointerException.
What you might find interesting, is (if I remember correctly), that for 1.4.2 onwards, you'll not get a NullPointerException, because the verifier was altered to allow fields to be initialised before calling the superclass constructor (note you can't do this yourself as the compiler won't allow it, but the compiler can do it when turning an inner class into a normal class). In this case, foo would still be null, but enclosingInstance wouldn't be.
