我有以下代码。我想了解创建内部类对象inner所使用的外部类对象。我该怎么做?
public class OuterClass {
public class InnerClass {
private String name ="Peakit";
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
InnerClass inner = outer.new InnerClass();
// How to get the same outer object which created the inner object back?
OuterClass anotherOuter = ?? ;
if(anotherOuter == outer) {
System.out.println("Was able to reach out to the outer object via inner !!");
} else {
System.out.println("No luck :-(");
}
}
}
编辑:好吧,你们中的一些人建议通过添加一个方法来修改内部类:
public OuterClass outer() {
return OuterClass.this;
}
号
但是如果我没有修改内部类的控制权,那么(只是确认一下)我们是否有其他方法从内部类对象中获取相应的外部类对象呢?
在内部类本身中,可以使用OuterClass.this。这个表达式允许引用任何词汇封闭的实例,在JLS中被描述为限定的this。
不过,我认为没有办法从内部类的代码之外获取实例。当然,您可以随时介绍自己的财产:
public OuterClass getOuter() {
return OuterClass.this;
}
编辑:通过实验,保存对外部类的引用的字段似乎具有包级访问权限——至少对于我使用的JDK是这样的。
编辑:使用的名称(EDCOX1(2))在Java中实际上是有效的,尽管JLS阻止了它的使用:
The $ character should be used only in
mechanically generated source code or,
rarely, to access pre-existing names on
legacy systems.
号
谢谢乔恩!但是如果我没有修改内部类的控制权(检查我的编辑)。
@匹克伊特:据我所知,除非你用反省,否则你就走运了。这感觉像是对封装的侵犯——如果内部类不想告诉您它的外部实例是什么,您应该尊重它,并尝试设计出不需要它的类。
这在Java 8中仍然有效吗?
OuterClass.this引用了外部类。
但只有在外环的源头内。我不认为这就是手术室想要的。
你可以(但不应该)用反省来做这份工作:
import java.lang.reflect.Field;
public class Outer {
public class Inner {
}
public static void main(String[] args) throws Exception {
// Create the inner instance
Inner inner = new Outer().new Inner();
// Get the implicit reference from the inner to the outer instance
// ... make it accessible, as it has default visibility
Field field = Inner.class.getDeclaredField("this$0");
field.setAccessible(true);
// Dereference and cast it
Outer outer = (Outer) field.get(inner);
System.out.println(outer);
}
}
号
当然,隐式引用的名称是完全不可靠的,所以如我所说,您不应该:—)
这个问题的更一般的答案涉及隐藏变量以及如何访问它们。
在下面的示例中(来自Oracle),main()中的变量x正在隐藏test.x:
class Test {
static int x = 1;
public static void main(String[] args) {
InnerClass innerClassInstance = new InnerClass()
{
public void printX()
{
System.out.print("x=" + x);
System.out.println(", Test.this.x=" + Test.this.x);
}
}
innerClassInstance.printX();
}
public abstract static class InnerClass
{
int x = 0;
public InnerClass() { }
public abstract void printX();
}
}
运行此程序将打印:
x=0, Test.this.x=1
。
更多信息,请访问:http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html jls-6.6
/**
* Not applicable to Static Inner Class (nested class)
*/
public static Object getDeclaringTopLevelClassObject(Object object) {
if (object == null) {
return null;
}
Class cls = object.getClass();
if (cls == null) {
return object;
}
Class outerCls = cls.getEnclosingClass();
if (outerCls == null) {
// this is top-level class
return object;
}
// get outer class object
Object outerObj = null;
try {
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
if (field != null && field.getType() == outerCls
&& field.getName() != null && field.getName().startsWith("this$")) {
field.setAccessible(true);
outerObj = field.get(object);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return getDeclaringTopLevelClassObject(outerObj);
}
当然,隐式引用的名称是不可靠的,因此您不应该为该作业使用反射。
public class Outer {
public Inner getInner(){
return new Inner(this,this.getClass());
}
class Inner {
public Inner(Outer outer, Class extends Outer> aClass) {
System.out.println(outer);
System.out.println(aClass.getName());
System.out.println();
}
}
public static void main(String[] args) {
new Outer().getInner();
}
}
。
如果您没有修改内部类的控件,那么refection可能会帮助您(但不推荐)。这个$0是内部类中的引用,它告诉外部类的哪个实例被用来创建内部类的当前实例。
我就是这样做的:
public class CherryTree {
public class Cherry {
public final CherryTree cherryTree = CherryTree.this;
// [...]
}
// [...]
}
。
当然,您需要能够修改内部类,并且每个获得内部类对象的人现在都可以访问外部类对象。就我而言,一切都很好。
示例如下:
// Test
public void foo() {
C c = new C();
A s;
s = ((A.B)c).get();
System.out.println(s.getR());
}
// classes
class C {}
class A {
public class B extends C{
A get() {return A.this;}
}
public String getR() {
return"This is string";
}
}