32

如果我有一个内部类的实例,如何从不在内部类中的代码访问外部类?我知道在内部类中,我可以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; }
  }
}
4

8 回答 8

33

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

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

以下是使用反射时示例代码的外观。男人,太丑了 ;)

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();
    }
}
于 2009-04-18T15:32:29.773 回答
14

没有办法,设计使然。如果您需要通过内部类的实例访问外部类,那么您的设计是倒退的:内部类的要点通常只在外部类中使用,或者通过接口使用。

于 2009-04-18T14:52:20.307 回答
8

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

于 2009-04-18T15:41:55.430 回答
6

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

我建议制作一个通用接口(命名内部类并非绝对需要,但可以“instancedof”/强制转换为):

public interface InnerClass<Outer> {
    Outer getOuter();
}

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

然后你做类似的事情:

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

这样您就可以从任何实现InnerClass<Outer>接口的内部类访问外部类(并检查它是否实际实现了它)。

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

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

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

如果 javac 自动在所有内部类上实现某种InnerClass<Outer>接口会容易得多,并且即使在匿名类上它也可以非常高效地做到这一点(没有缓慢的内省处理)!

于 2012-07-14T10:48:06.987 回答
1

如果您有一个(非静态)内部类,那么根据定义,您正在使用仅在外部类的封闭上下文中起作用的东西。因此,要获得内部类的句柄,您已经必须通过外部实例检索它。所以我能看到的唯一方法就是你需要一个这样的访问器是通过外部引用抓住内部,然后丢失或丢弃外部实例引用。

例如:

public class Outer{ 
   public class Inner {
   }
}

public class UsesInner{
 Collection<Outer.Inner> c = new ArrayList<Outer.Inner>();
}

现在,您可以填充 c 的唯一方法是创建 Outer() 实例。我很想看到这样的有用目的。

于 2009-04-18T15:53:29.693 回答
1

这是您可能需要此行为的一个原因:您拥有内部class实例,并且需要class使用反射访问外部定义的方法。

为了记录,inner.getClass().getDeclaredField("this$0")工作。既然是字段private,你也需要调用field.setAccessible(true)

于 2009-11-03T21:58:08.940 回答
1

简单地写这个

class Outer {
  class Inner {
    Outer outer = Outer.this;
  }
}
于 2013-01-29T17:22:47.857 回答
0

你不能做这样的事情:

public static class Inner { // static now
    private final Outer parent;

    public Inner(Outer parent) { this.parent = parent; }

    public Outer get Outer() { return parent; }
}
于 2009-04-18T15:46:23.533 回答