2006年4月18日星期二
Reflection & inner classes (zz)
In case you'll ever have to work with non-static inner classes and reflection here are two tips:
(1) Creation
Inner classes need an instance of its direct enclosing class for creation. If you know the types, this would look like.
Outer outerObj = ...
Outer.Inner innerObj = outerObj.new Inner();
or something similar.
Now with reflection you have to somehow supply this outer instance to the constructor. Therefore, Java implicitly adds an additional first parameter to every constructor of the inner class, even the default one. In practice this means that there is no no-arg constructor and hence Class#newInstance() can't be used with inner classes (which is somewhat obvious, come to think of it).
So doing the above via reflection would look like:
Outer outerObj = ...
Constructor constructor = Outer.Inner.class.getConstructor(new Class[]{Outer.class});
Outer.Inner innerObj = (Outer.Inner)constructor.newInstance(new Object[] {outerObj});
(2) Getting the reference to the enclosing class
Again something simple when you know the classes:
public class Outer
{
public class Inner
{
public void doSomething()
{
Outer outerObj = Outer.this;
}
}
}
As you can see, Java has a qualified extension to the this reference that specifies which this reference in the current context is meant.
In reflection this is a bit more difficult, because you have to know how this reference is encoded in bytecode. Java uses the name this${n} where {n} specifies the level of the class.
public class Outer
{
public class Inner
{
public class Deep
{
}
}
}
In this hierarchy, this$0 is the this reference of the topmost enclosing class Outer and this$1 to Inner.
So in order to access Outer and Inner from within Deep, we would do:
public class Outer
{
public class Inner
{
public class Deep
{
public void doSomething()
{
Outer outerObj = (Outer)getClass().getDeclaredField("this$0").get(this);
Inner innerObj = (Inner)getClass().getDeclaredField("this$1").get(this);
}
}
}
}
Note that you have to use getDeclaredField because this field is private. Likewise you might have to use setAccessible(true) on the field if you're using this in an environment with a security manager.

没有评论