Tuesday, July 28, 2009

Getting references to outer classes

Sometimes it is necessary to get references to the outer classes of an inner class. From within the inner class it is pretty straightforward to obtain the reference to the outer class. Just precede the keyword this with the outer class name. Here is a simple example:

public class OuterThis {
private String me = "OuterThis";
public class Inner1 {
private String me = "Inner1";
public class Inner2{
public void getEnclosingClasses() {
System.out.println(Inner1.this.me);
System.out.println(OuterThis.this.me);
}
}
}

public static void main(String[] args) {
OuterThis.Inner1.Inner2 in2 = new OuterThis().new Inner1().new Inner2();
in2.getEnclosingClasses();
}

}


You can also get the outer class reference through reflection. The this reference is encoded as this$n where n is an int specifying the level of the class. In the example above, this$0 is the outer class "OuterThis" while this$1 refers to "Inner1". So in the code above just do the following to get the enclosing class "Inner1" through reflection in the method getEnclosingClasses().
Inner1 in1 = (Inner1)getClass().getDeclaredField("this$1").get(this);

Reflection to obtain the outer class reference is usually used when you are outside the inner class. The following example illustrates it.

import java.util.ArrayList;

public class ExposeThis {
private String exposeThis = "Just Expose This";

public static void main(String[] args) throws
IllegalArgumentException, SecurityException, NoSuchFieldException,
IllegalAccessException {
Client cl = new Client();
new ExposeThis().registerMe(cl);
cl.printThis();
}

public void registerMe(Client client) {
client.addListener(new ClientListener(){});
}

public String toString() {
return exposeThis;
}

}

interface ClientListener {
}

class Client {
private ArrayList<ClientListener> clients = new ArrayList<ClientListener>();

public void addListener(ClientListener cl) {
clients.add(cl);
}

public void printThis() throws
NoSuchFieldException, IllegalArgumentException,
SecurityException, IllegalAccessException{
for (ClientListener cl: clients) {
//Get handle to OuterClass this by reflection
ExposeThis et =
(ExposeThis)cl.getClass().getDeclaredField("this$0").get(cl);
System.out.println(et);
}
}
}





Note:
Since it is possible to obtain references to enclosing classes by getting a handle to the inner class, one has to be very careful while working with inner classes.

1 comment: