在Java中,当我不在内部类时,如何访问外部类?

Kip*_*Kip 32 java syntax nested

如果我有一个内部类的实例,我如何从不在内部类中的代码访问外部?我知道在内部类中,我可以Outer.this用来获取外部类,但我找不到任何外部方法来获取它.

例如:

public class Outer {
  public static void foo(Inner inner) {
    //Question: How could I write the following line without
    //  having to create the getOuter() method?
    System.out.println("The outer class is: " + inner.getOuter());
  }
  public class Inner {
    public Outer getOuter() { return Outer.this; }
  }
}
Run Code Online (Sandbox Code Playgroud)

Esk*_*ola 32

Outer$Inner该类的字节码将包含一个名为this$0type 的包范围字段Outer.这就是用Java实现非静态内部类的方式,因为在字节码级别没有内部类的概念.

如果你真的想,你应该能够使用反射读取该字段.我从来没有任何需要这样做,所以你最好改变设计,这样就不需要了.

以下是使用反射时示例代码的外观.伙计,那很难看.;)

public class Outer {
    public static void foo(Inner inner) {
        try {
            Field this$0 = inner.getClass().getDeclaredField("this$0");
            Outer outer = (Outer) this$0.get(inner);
            System.out.println("The outer class is: " + outer);

        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public class Inner {
    }

    public void callFoo() {
        // The constructor of Inner must be called in 
        // non-static context, inside Outer.
        foo(new Inner()); 
    }

    public static void main(String[] args) {
        new Outer().callFoo();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 那是(IIRC)编译器依赖的,你通常需要一个setAccessible. (2认同)
  • 当有多个嵌套级别时不起作用。因为在这种情况下,该字段可以命名为“this$1”。 (2认同)

Mic*_*rdt 14

没有办法,按设计.如果需要通过内部类的实例访问外部类,那么您的设计是向后的:内部类的点通常仅在外部类中使用,或者通过接口使用.

  • 这也是我的想法,但我似乎无法找到任何解决此限制的文档.如果你能找到任何官方的话,我很乐意接受这个答案. (2认同)
  • 15.8.3和15.8.4涵盖了它."任何词汇封闭的实例都可以通过明确限定关键字this来引用." 在问题的情况下,它不是一个词汇封闭的实例. (2认同)

sta*_*lue 8

在需要访问外部类时添加getter有什么问题?这样您就可以控制是否允许访问.


小智 6

事实上,这是一个非常好的问题,例如,如果您需要能够检查Outer类的两个不同实例是否共享同一个类实例InnerClass<Outer>(==或等于取决于上下文).

我建议创建一个通用接口(对于命名的内部类不是绝对必需的,但可以是"instancedof"/ casted to):

public interface InnerClass<Outer> {
    Outer getOuter();
}
Run Code Online (Sandbox Code Playgroud)

可以应用于任何命名的内部类.

然后你做的事情如下:

class MyInnerClass implements InnerClass<Outer> {
    Outer getOuter() {
        return Outer.this;
    }
    // remaining implementation details
}
Run Code Online (Sandbox Code Playgroud)

这样你就可以从任何实现InterfaceOrAbstractClass接口的内部类访问外部类(并检查它实际实现了它).

如果你的内部类是匿名的,你只能这样做(感谢Rich MacDonald的样本):

new InterfaceOrAbstractClass<Outer>() {
    Outer getOuter() { // super inefficient but this is the only way !
        return (Outer)getClass().getDeclaredField("this$0");
    }
    /* other methods */
}
Run Code Online (Sandbox Code Playgroud)

InnerClass<Outer> 必须实现getOuter()能够访问InnerClass<Outer>匿名类体外!

如果javac自动Outer在所有内部类上实现某种接口,那么它就会变得容易得多,即使在匿名类上也可以非常有效地执行(没有缓慢的内省处理)!