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

Posted

技术标签:

【中文标题】从内部类对象中获取外部类对象【英文标题】:Getting hold of the outer class object from the inner class object 【发布时间】:2010-12-21 10:45:44 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

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

我不认为有办法从内部类的代码之外获取实例。当然,你可以随时引入自己的属性:

public OuterClass getOuter() 
    return OuterClass.this;

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

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

$ 字符只能用于 机械生成的源代码,或者, 很少,访问预先存在的名称 旧系统。

【讨论】:

谢谢乔恩!但是如果我无法控制修改内部类怎么办(检查我的编辑)。 @peakit:那么据我所知,除非你使用反射,否则你就不走运了。感觉它确实违反了封装——如果内部类不想告诉你它的外部实例是什么,你应该尊重它并尝试设计成你不需要它。 这在 Java 8 中仍然有效吗? @misty 是的。【参考方案2】:

OuterClass.this 引用外部类。

【讨论】:

但仅在 OuterClass 的源中/之内。而且我认为这不是 OP 想要的。【参考方案3】:

您可以(但不应该)在工作中使用反射:

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

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

【讨论】:

【参考方案4】:

这个问题的更一般的答案涉及阴影变量以及如何访问它们。

在以下示例中(来自 Oracle),ma​​in() 中的变量 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

【讨论】:

不确定该示例是否能最好地证明这一点,因为“Test.this.x”与“Test.x”相同,因为它是静态的,并不真正属于封闭类对象。如果代码在类 Test 和 Test.x 的构造函数中不是静态的,我认为会是更好的例子。【参考方案5】:

示例如下:

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

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:
/**
 * 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);

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

【讨论】:

'静态内部'是一个矛盾的术语。

以上是关于从内部类对象中获取外部类对象的主要内容,如果未能解决你的问题,请参考以下文章

在静态方法中new 一个内部类对象和new 一个外部类对象的区别

GroovyMOP 元对象协议与元编程 ( Groovy 类内部和外部分别获取 metaClass | 分析获取 metaClass 操作的字节码 | HandleMetaClass 注入方法 )

面向对象---内部类

内部类

内部类

java 内部类