Abstract: A quick follow-up to the previous newsletter, to show how the ThisEscape class is compiled, causing the "this" pointer to leak.
When I posted the previous newsletter, I did not explicitly show how the "this" reference escapes implicitly. I have been showing these mysteries in my Java courses since 1999 and I forgot that there might be some mortals who have never seen a decompiled inner class ;-)
javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.
When you construct a inner class within a non-static context, such as in a non-static method, a constructor or an initializer block, the class always has a pointer to the outer object.
Here again is our class ThisEscape:
import java.util.*; public class ThisEscape { private final int num; public ThisEscape(EventSource source) { source.registerListener( new EventListener() { public void onEvent(Event e) { doSomething(e); } }); num = 42; } private void doSomething(Event e) { if (num != 42) { System.out.println("Race condition detected at " + new Date()); } } }
When it gets compiled, javac generates two classes. The outer class looks like this:
import java.util.*; public class ThisEscape { private final int num; public ThisEscape(EventSource source) { source.registerListener(new ThisEscape$1(this)); num = 42; } private void doSomething(Event e) { if (num != 42) System.out.println( "Race condition detected at " + new Date()); } static void access$000(ThisEscape _this, Event event) { _this.doSomething(event); } }
Note how the compiler added the static, package access method
access$000()
. Note also how it passed a pointer
to this
into the constructor for the
anonymous inner class ThisEscape$1
.
Next we have the anonymous inner class. It is package
access, with a package access constructor. Internally it
maintains a reference to the ThisEscape object. Note:
the anonymous class sets the this$0
field
before calling super()
. This is the only
place where this is allowed in Java.
class ThisEscape$1 implements EventListener { final ThisEscape this$0; ThisEscape$1(ThisEscape thisescape) { this$0 = thisescape; super(); } public void onEvent(Event e) { ThisEscape.access$000(this$0, e); } }
Hopefully it is now clearer how the "this" pointer is implicitely leaked.
Kind regards from Crete
Heinz
We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)
We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.