关于java:从内部类对象获取外部类对象

Getting hold of the outer class object from the inner class object

我有以下代码。我想了解创建内部类对象inner所使用的外部类对象。我该怎么做?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 :-(");
        }
    }
}

编辑:好吧,你们中的一些人建议通过添加一个方法来修改内部类:

1
2
3
public OuterClass outer() {
   return OuterClass.this;
}

但是如果我没有修改内部类的控制权,那么(只是确认一下)我们是否有其他方法从内部类对象中获取相应的外部类对象呢?


在内部类本身中,可以使用OuterClass.this。这个表达式允许引用任何词汇封闭的实例,在JLS中被描述为限定的this

不过,我认为没有办法从内部类的代码之外获取实例。当然,您可以随时介绍自己的财产:

1
2
3
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.


OuterClass.this引用了外部类。


你可以(但不应该)用反省来做这份工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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();
    }
}

运行此程序将打印:

1
x=0, Test.this.x=1

更多信息,请访问:http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html jls-6.6


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
 * 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);
}

当然,隐式引用的名称是不可靠的,因此您不应该为该作业使用反射。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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是内部类中的引用,它告诉外部类的哪个实例被用来创建内部类的当前实例。


我就是这样做的:

1
2
3
4
5
6
7
public class CherryTree {
    public class Cherry {
        public final CherryTree cherryTree = CherryTree.this;
        // [...]
    }
    // [...]
}

当然,您需要能够修改内部类,并且每个获得内部类对象的人现在都可以访问外部类对象。就我而言,一切都很好。


示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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";
   }
}