【问题标题】:In Java, how do I access the outer class when I'm not in the inner class?在Java中,当我不在内部类中时,如何访问外部类?
【发布时间】:2010-10-20 07:07:57
【问题描述】:

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

【问题讨论】:

  • 您能解释一下您要解决的问题吗?还是这是学术?
  • 学术,主要是。当我想出一个答案时,我遇到了它:stackoverflow.com/questions/763359/… 我给出了两个答案,一个是按照 OP 的要求使用我上面使用的相同解决方法(getOuter() 方法)。但是在我的另一个答案(已被投票的那个)中,我说要以不同的方式设计它,这样就没有必要了。但我仍然很好奇是否有可能做到这一点。
  • 您提供的示例是此问题的最佳答案。

标签: java syntax nested


【解决方案1】:

Outer$Inner 类的字节码将包含一个名为this$0 类型为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();
    }
}

【讨论】:

  • 这取决于 (IIRC) 编译器,您通常需要一个 setAccessible。
  • 我认为(懒得查阅规范)即使该字段的命名也是依赖于编译器的,所以如果您尝试与另一个 hack 进行破解,您可能会遇到问题编译器。
  • 我正在阅读 Java Concurrency in Practice,其中一个示例展示了 this 引用如何与内部类一起转义,如果我们将内部类的实例传递给构造函数中的一些外来代码。我正在尝试编写在构造函数返回之前实际上可以保留引用的外星人代码。非常感谢您的回答。
  • 在有多层嵌套时不起作用。因为在这种情况下,该字段可以具有名称 this$1
【解决方案2】:

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

【讨论】:

  • 这也是我的想法,但我似乎找不到任何解决此限制的文档。如果你能找到任何官方的东西,我很乐意接受这个答案。
  • 这不是一个明确的“限制”——为什么要有关于特定(相当晦涩)功能缺失的文档?
  • 15.8.3 和 15.8.4 涵盖了它。 “任何词法封闭的实例都可以通过明确限定关键字 this 来引用。”在问题的情况下,它不是一个词法封闭的实例。
  • 如何从 JNI 访问它,即通过 native 类的 Inner 方法?
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

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

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

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

    然后您执行以下操作:

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

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

    如果您的内部类是匿名的,您只能这样做(感谢 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&lt;Outer&gt;才能在匿名类主体之外访问getOuter()

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

    【讨论】:

      【解决方案5】:

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

      例如:

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

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

      【讨论】:

      • 老实说,我也想不出一个好的理由。我刚碰到它试图回答这个问题:stackoverflow.com/questions/763359/… 在这种情况下,他想确保他有一个外部类的特定实例的内部类。我在另一个答案中建议他应该只在内部类中定义方法,这样就不会出现问题。但是,再次,我很好奇是否有办法做到这一点。
      【解决方案6】:

      简单写一下

      class Outer {
        class Inner {
          Outer outer = Outer.this;
        }
      }
      

      【讨论】:

      • 这样写,很难说是答案还是新问题。
      【解决方案7】:

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

      作为记录,inner.getClass().getDeclaredField("this$0") 有效。由于字段为private,所以还需要调用field.setAccessible(true)

      【讨论】:

        【解决方案8】:

        你不能这样做吗:

        public static class Inner { // static now
            private final Outer parent;
        
            public Inner(Outer parent) { this.parent = parent; }
        
            public Outer get Outer() { return parent; }
        }
        

        【讨论】:

        • 去掉静态,使用:Outer getOuter(){ return Outer.this; }
        猜你喜欢
        • 1970-01-01
        • 2013-05-07
        • 2013-07-08
        • 1970-01-01
        • 1970-01-01
        • 2011-01-02
        • 2021-11-27
        • 1970-01-01
        相关资源
        最近更新 更多