Reflection On Inner Classes

There seems to be no way to obtain a reference to the outer instance of an instance of an InnerClass using Java reflection.

For example an outer and inner class:

 public class A {
     public class B {
         private int i;
     }
 }
Some code that creates instances of these classes:
 A a = new A();
 A.B b = a.new B();
Using reflection on 'b', one can access to the private field 'i', but how does one obtain a reference to back 'a' (or A.this as it would be named inside 'b')?

???

It's easy -- as long as depending on undocumented stuff doesn't bother you. ;->

Try this:

 package reflection_on_inner_class;
 public class A {
     public class B {
         private int i;
     }
 }

package reflection_on_inner_class; import java.lang.reflect.Field; import junit.framework.TestCase; public class ReflectAB extends TestCase { public void testAccessToOuterClass() throws Exception { final A a = new A(); final A.B b = a.new B(); final Field parent = A.B.class.getDeclaredField("this$0"); final Object a2 = parent.get(b); assertSame(a, a2); } }
And 'this$0' isn't even 'private'! Enjoy! -- JeffGrigg

Thanks. That is a hack around the problem that I can use to get things working. This does highlight a flaw in the reflection API though.

A flaw? In that 'this$0' should be private? Or are you suggesting that an (non-static) inner class' pointer to its parent class should have a well defined and documented name?

It's a flaw that the relationship between inner and outer object is not represented in the public reflection API. You can use a hack to do the navigation but it requires more trickery than shown above; the n of the this$n variable changes depending on how deeply nested the class is. Also, there is no guarantee that this will be supported in future versions of Sun's Java.

A better solution would be for Class objects to have a public method for navigating inner/outer class and instance relationships: Object getOuterInstance(Object o).


I'm trying to write code that reflects on objects and discovers their structure. I can't ask everybody in the entire world to replace their inner classes with static inner classes that have an explicit reference to the outer scope in a field with a name that I've decided, can I?

The inner class need not be static. Use reflection to initialize "classA" in the outer class. The value of classA is now available to any inner class, static or not. If you're trying to this on classes that you can't change, you can write adaptors on each and hold the needed value in the adapter for class A, and reference it from the adaptor of inner class.

I think you're missing the point. I'm using reflection to examine the structure of existing objects that are written by other people. I can't force them to change their code. The tool needs to be able to examine any code that can be thrown at it, so I cannot know what could would have to be changed if I was even able to change it.

That is, after all, the whole point of the reflection API!

Can you please offer a specific example of something you're trying to with existing classes (and inner classes), how you would use it if reflection did what you want, and how using an adaptor pattern as I suggested above doesn't work? A specific example is often more instructive than hypothetical scenarios.

I'm trying to reflect on third-party code to work out if an object is a "callback" to another object. E.g. B is a callback to A if it is A or it is an instance of an inner class of A.class and the outer instance is A. I don't know in advance what code I will have to reflect upon. So, I can't force people writing the code to change their code to reflect my needs.

I can only offer pseudo-code, because I've been writing Javascript, Python, Perl and Smalltalk for years now and my Java is rusty. Here's the general idea, though. Define classes WrapperA and WrapperB, such that each instance of WrapperA contains a reference to WrapperB (WrapperB.class), and WrapperA and WrapperB each create and maintain a reference to A and B, respectively. Hook the call that creates B so that it creates WrapperB instead. Now perform your comparison with WrapperA and WrapperB in addition to A and B. Specifically, the instance of WrapperB can reference the variable defined in WrapperA (because an inner class can reference members defined in its container). I think this gets you where you want to go without modifying A and B. -- TomStambaugh

I'm confused. Third party code is creating an object and my code has to reflect upon it. How do I make the third-party code create WrapperA and WrapperB classes instead of A and B classes? If I cannot, I have to work out myself whether I need to create WrapperA and WrapperB classes by determining if the B object is an instance of an inner class and then somehow getting the outer A object from the B object... which leaves me back where I started.

Third party code uses constructors for A and B. You are able to identify and call those same constructors. You are equally able to write and call constructors for WrapperA and WrapperB that call those same constructors. If you want to be really sleazy, you can use various tricks to introduce your own class code instead of A and B (such as manipulating the class path) and use a variant of one of the active value patterns (use reflection to capture the parameters of the call, do your thing, then make the call(s) to the real classes). -- TomStambaugh

I'm not explaining myself very well. I'll try again with more detail.

To emphasise: my code is under the control of the third-party code. I cannot change the third-party code. I cannot even know what code is going to be plugging in to my library. I have no idea what that code is going to pass in. Indeed, it must be left open so my library can generically process any kind of object created by currently unknown code.

I can't see anyway to create wrappers that does not involve first solving the very same reflective problem that the wrappers are intended to solve.

The code is accessible to you in some form, such as a jar file or a directory of class files. You have a rich variety of ways to examine those in addition to simply running the third party code. How does the code pass "a" or "b" to your code, if you have no control over it? How are the "a" or "b" (I assume you mean instances of classes A and B, respectively) instances created? I appreciate that this may be tricky and counter-intuitive -- I never claimed to like JavaLanguage, especially its abysmal metastructure. My only claim is that this specific problem seems solvable.

I've put as much effort into this exchange as I'm willing to spend. You have a plugin, you have running code, you have a framework, the bytecodes are all you need. I didn't say it was easy. Metastructure is never easy, and the tangled mess of the Java reflection API makes an already hard problem worse. Perhaps one of the Java wizards here can help out.

I think we're in violent agreement.

Of course it's possible. However, it's far too hard to achieve and there are far too many tricky corner cases that require knowledge of undocumented internals of the Java compiler. The relationships between inner and outer objects should be represented in the reflection API just like the relationships between inner and outer classes already are.

By the way, it's not always possible to get hold of the bytecode of a class. For example, bytecode can be received over the network into an array of bytes, a class loaded from that array of bytecode, and then the bytecode is garbage collected.


Ah; well, Java inner classes do seem to be something of a "hack," themselves -- a jumble of naming conventions that aren't part of any official standard. (At least none that I know of.) Well, such is life.

I'd suggest just coding for their funny naming conventions in your code, and accept that if they change the conventions in the future, you'll have to change your code. -- JeffGrigg

I do not consider this ReflectionOnInnerClasses a flaw of the Java. Thats because I consider Java and the JavaVm? two distinct systems. And reflection is not so much a part of Java then of the VM. Reflection really doesn't reflect Java objects and classes, but the VMs view of JavaByteCode and there no inner classes exist. There do not even exist constructors, but this is hidden by the reflection API and that is a flaw (or at least an inconsistency). -- GunnarZarncke

?

Sorry. I should have been more to the point of your question. JavaReflection? can only reflect what exists in the JavaByteCode. And there inner classes do not exist. Inner classes are compiled as all normal java classes too, but they just have some default "parent" variable (the "this$0" mentioned above). Also the "containing" class contains special methods, that allow the "inner" class to reference private members.

As for the not existing constructors. In JavaByteCode there exists only one single constructor and that is called "new". But after it special methods called "<init>" may be called, which realize the java constructors. Have a look at e.g. http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html.

It's easy to confuse Java and the JavaVm?, because they are so ExtremelyIntertwingled?. But there are a lot OtherLanguagesForTheJavaVm, that you can use and reflect on. -- GunnarZarncke

The inner class, B, has a constructor. It takes an instance of 'A' as a parameter. I verified this by adding these lines to the test above:

       final Constructor constructor = A.B.class.getConstructor(new Class[] { A.class });
       final A.B b2 = (A.B) constructor.newInstance(new Object[] { a });
       final Object a3 = parent.get(b2);
       assertSame(a, a3);
-- JeffGrigg

I find GunnarZarncke's argument a bit specious. After all, there is no concept of nested classes at the JVM level. Nested classes are implemented by naming conventions -- extra "$" symbols in class names. However, nested classes are represented in the reflection API. So if the name mangling for nested classes can be given a clean API why not the name mangling for the references to outer "this" instances? It looks like an oversight, not a design decision.


AprilZeroSix

CategoryJava


EditText of this page (last edited April 13, 2006) or FindPage with title or text search