从内部类对象获取外部类对象

284

我有以下代码。我想通过创建内部类对象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;
}

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

7个回答

375

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

不过我认为在内部类外部无法获取实例。当然,您始终可以引入自己的属性:

public OuterClass getOuter() {
    return OuterClass.this;
}

编辑:通过实验,看起来持有对外部类引用的字段具有包级访问权限 - 至少对于我正在使用的JDK而言。

编辑:在Java中,所使用的名称(this$0实际上是有效的,尽管JLS不鼓励它的使用:

$字符应该仅用于机械生成的源代码或者极少数情况下,以访问遗留系统上的预先存在的名称。


谢谢Jon!但是如果我没有修改内部类的控制权怎么办(请查看我的编辑)? - peakit
8
@peakit:据我所知,除非使用反射,否则你将没有办法。但这似乎违反了封装的原则——如果内部类不想告诉你它的外部实例是什么,你应该尊重它,并尝试设计使你不需要它。 - Jon Skeet
1
这在Java 8中仍然有效吗? - misty
1
@misty 是的,没错。 - Hatefiend

41

OuterClass.this 引用外部类。


12
但仅限于OuterClass源代码内部。我认为这不是原帖作者想要的。 - Stephen C
2
这并没有回答问题。 - jjazzboss

26

你可以(但不应该)使用反射来完成这项工作:

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);
    }
}

当然,隐式引用的名称是完全不可靠的,所以正如我所说,你不应该使用它 :-)

2
这个问题的更一般的答案涉及到被屏蔽的变量以及它们如何被访问。
在下面这个例子(来自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

这段内容涉及IT技术,建议阅读以上链接以深入了解。

2
不确定该示例是否最能证明这一点,因为“Test.this.x”与“Test.x”相同,因为它是静态的,实际上并不属于封闭类对象。我认为如果代码在Test类的构造函数中,并且Test.x不是静态的,那么会更好地说明问题。 - sb4

0

这是一个例子:

// 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";
   }
}

0

如果您无法控制修改内部类,则反射可能会帮助您(但不建议)。this$0是内部类中的引用,它告诉当前内部类实例是使用哪个外部类实例创建的。


-2
/**
 * 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);
}

当然,隐式引用的名称是不可靠的,因此您不应该使用反射来完成这项工作。


"静态内部"这个词本身就是一个自相矛盾的说法。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接