【问题标题】:Java inner class visibility puzzleJava 内部类可见性难题
【发布时间】:2009-07-06 09:48:14
【问题描述】:

考虑以下情况:

public class A {
  public A() { b = new B(); }
  B b;
  private class B { }
}

从 Eclipse 中的警告中,我引用:java 编译器通过合成访问器方法模拟构造函数 A.B()。我想编译器现在继续为 B 创建一个额外的“水下”构造函数。

我觉得这很奇怪:为什么 B 级不会像 a.k.o. 一样可见。 A 中的字段? 并且:这是否意味着 B 类在运行时不再是私有的? 还有:为什么 B 类的 protected 关键字的行为不同?

public class A {
  public A() { b = new B(); }
  B b;
  protected class B { }
}

【问题讨论】:

    标签: java inner-classes


    【解决方案1】:

    内部类本质上是 Java 1.1 中引入的一种 hack。 JVM 实际上并没有任何内部类的概念,因此编译器不得不拒绝它。编译器在 A 类“外部”生成 B 类,但在同一个包中,然后向它添加合成访问器/构造器以允许 A 访问它。

    当你给 B 一个受保护的构造函数时,A 可以访问该构造函数,因为它在同一个包中,而无需添加合成构造函数。

    【讨论】:

    • 好的,我明白了。对我来说,这意味着我将避免像示例中那样使用内部类,这只会导致混乱。
    • 我不会让它打扰你的。那个特定的编译器警告对任何人都没有多大用处,合成方法一直用于内部类,并且没有重大影响。
    • 恕我直言,合成方法是对语言的不必要添加。仅对“私有”成员使用包范围(无论如何编译器都会在后台执行)是一个令人满意的解决方案。
    • @CarlosHeuberger 我认为这不是“全部真相”,请参阅我刚刚发布的answer...
    【解决方案2】:

    我知道这个问题现在已经快三年了,但我发现部分问题仍然没有回答:

    还有:这是否意味着 B 类在运行时不再是私有的?

    Carlos Heubergers 对 skaffmans 答案的评论表明,对于包中的其他类,B 类仍然是 private

    他可能适合 Java 编程语言,即不可能从其他类引用类 B。至少不能不使用反射(也可以从外部访问私有类成员),但这是另一个问题。

    但是由于 JVM 没有任何内部类的概念(如 skaffman 所述),我问自己如何在字节码级别实现“仅一个类可访问”的可见性。答案:根本没有实现,对于JVM来说,内部类看起来就像一个普通的包私有类。也就是说,如果您自己编写字节码(或修改编译器生成的字节码),您可以毫无问题地访问类B

    您也可以从同一个包中的所有类访问所有合成访问器方法。因此,如果您在类B 的方法中为类A 的私有字段分配值,则在类A 中生成具有默认(即包私有)可见性的合成访问器方法(命名为access$000 ) 为您设置值。这个方法应该只能从类B 调用(实际上它只能从那里使用Java 语言调用)。但从 JVM 的角度来看,这只是一个方法,可以被任何类调用。

    所以,回答这个问题:

    • 从 Java 语言的角度来看,B 类是私有的。
    • 从 JVM 的角度来看,B 类(或更好:A$B 类)不是私有的。

    【讨论】:

    • 正确,但这不是我的建议。我写了“members 仍然是私有的”——我的意思是类的字段,而不是你解释的 class 本身!这个问题也是关于java 而不是bytecode(生成、修改、hacking ...)。
    • @CarlosHeuberger 当然这对你没有冒犯!当然 B 类的成员仍然是私有的,但 B 类(作为 A 类的成员)不是(从 JVM 的角度来看),这是 Gerard 最初的问题。是的,问题是关于 java 的,但正如标签 wiki 中所述:“Java 是一种编程语言和 运行时环境”。而运行环境是JVM,而JVM本身与Java编程语言无关,只解释字节码。
    【解决方案3】:

    class B 的访问权及其构造函数不必相同。你可以有一个带有包作用域构造函数的私有内部类,这是我通常做的。

    public class A {
      public A() { b = new B(); }
      B b;
      private class B {
        B() { }
      }
    }
    

    【讨论】:

    • 但是如何才能拥有一个适当私有类的包私有实例呢?
    【解决方案4】:

    你需要使用

    this.new B();
    

    【讨论】:

    • 对不起,this.new B();给出相同的警告和行为。
    • @Rats:这对手头的问题没有影响。 “this”限定是隐含的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-26
    • 2020-08-29
    • 2015-11-24
    • 2023-03-11
    • 2018-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多