Question
When an inner class (N.B.: non static) is instantiated, it will get a reference to the outer class (upon its construction). Which guarantees apply in this case (if any) regarding the persistence of this reference?
Long explanation (trying to give a sense to the question)
Consider this simple code:
public class OuterInnerExample {
private int mInt;
public void startJob() {
InnerRunnable r = new InnerRunnable();
Thread t = new Thread(r);
t.start();
}
private class InnerRunnable implements Runnable {
public void run() {
int localInt = mInt;
System.out.println(localInt);
}
}
}
After compilation, if we decompile the class files (I used jad
), we can observe that InnerRunnable
has a this$0
member which is the reference to OuterInnerExample
object.
This reference is set in the compiler-synthesized constructor of InnerRunnable
, before calling super()
(so this avoids the situation in which this$0
may be null
when the object is used before its construction is completed, i.e., through the base class).
When InnerRunnable
wants to access mInt
, it will use a static getter synthesized by the compiler, that takes a reference to OuterInnerExample
as a parameter (this is going to be this$0
). This getter is the access$100
method.
For reference, the decompiled Java can be found here: http://pastebin.com/gr8GB03t.
Now, the whole problem is that I have observed (and cannot explain), a stack trace similar to the following:
FATAL EXCEPTION: main
java.lang.NullPointerException
at <package.name>.OuterInnerExample.access$100(<line where "class OuterInnerExample" is>
at <package.name>.OuterInnerExample$InnerRunnable.run(<line where "public void run()" is>)
which makes me think that this$0
must have been null
at that point.
To answer your question: as the JLS 8.1.3 states:
When an inner class (whose declaration does not occur in a static context) refers to an instance variable that is a member of a lexically enclosing class, the variable of the corresponding lexically enclosing instance is used.
Read JLS 17.4.5 about the happens-before relations:
The default initialization of any object happens-before any other actions (other than default-writes) of a program.
Default initialization here means the assignment of default values to fields.
Since the field private int mInt
is primitive it can't be null
so you must look elsewhere.
My guess is that you are using some custom compiler since this part:
private OuterInnerExample$InnerRunnable(OuterInnerExample outerinnerexample)
{
this$0 = outerinnerexample;
super();
}
leads to a compile-time error. You have to call super()
in the first line of a constructor.
I actually copy-pasted your original code and used java 1.7 to execute it and it worked without problems so we are missing context here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With