为什么嵌套子类可以访问其父类的私有成员,但孙子不能?

时间:2016-07-25 05:41:49

标签: java visibility inner-classes

可能类似问题Why can outer Java classes access inner class private members?Access to superclass private fields using the super keyword in a subclass

但是存在一些差异:子类可以访问其父级的私有成员(以及仅最近的父级)类。

给出以下示例代码:

public class T {

    private int t;

    class T1 {
        private int t1;

        public void test() {
            System.out.println(t);
        }
    }

    class T2 extends T1 {

        private int t2;

        public void test() {
            System.out.println(t);
            System.out.println(super.t1);
            System.out.println(this.t2);
        }
    }

    class T3 extends T2 {

        public void test() {
            System.out.println(t);
            System.out.println(super.t1); // NG: t1 Compile error! Why?
            System.out.println(super.t2); // OK: t2 OK
        }
    }
}

3 个答案:

答案 0 :(得分:50)

聪明的例子!但这实际上是一个有点无聊的解释 - 没有可见性问题,你根本无法直接从t1引用T3因为super.super isn't allowed

T2无法直接访问自己的t1字段,因为它是私有的(并且子类不会继承其父级的私有字段),但super实际上是{的实例{1}}由于它位于同一个班级T1,因此可以引用T2的私有字段。 super没有机制直接处理其祖父母类T3的私有字段。

这两个都在T1内编译得很好,这表明T3可以访问其祖父母的T3字段:

private

相反,这不会在System.out.println(((T1)this).t1); System.out.println(new T1().t1); T2中编译:

T3

如果允许System.out.println(t1); ,您可以从super.super执行此操作:

T3
  

如果我定义了3个类,System.out.println(super.super.t1); ABC具有受保护的字段At1继承自B的{​​{1}}和AC可以通过调用B来引用C s A,因为它这里可见。逻辑上应该不适用于内部类继承,即使该字段是私有的,因为这些私有成员应该是可见的,因为它们属于同一个类?

(为简单起见,我将坚持使用OP的t1super.t1T1类名称。

如果T2T3,则没有任何问题 - t1可以直接引用protected字段,就像任何子类一样。问题出现在T3,因为某个类没有感知其父类的t1字段,因此无法直接引用它们,即使在实践中它们是可见的。这就是为什么你必须使用private中的private,以便甚至引用相关字段。

即使super.t1涉及它没有T2字段,它也可以通过位于同一个外部类中访问T3t1字段。既然如此,您需要做的就是将T1转换为private,并且您可以引用私有字段。 this中的T1电话(实质上)将super.t1投射到T2,让我们引用其字段。

答案 1 :(得分:15)

合成辅助方法

从技术上讲,在 JVM 级别,您可以访问另一个类的任何private成员 - 也不能访问封闭类的成员{{1} }),也不是父类(T.t)的那些。在您的代码中,您可以看起来像,因为编译器会在访问的类中为您生成synthetic访问器方法。使用正确的表单T2.t2T3课程中修复无效的引用 super.t1 时,会发生同样的情况。

借助此类编译器生成的 ((T1) this).t1 访问器方法,您通常可以访问 的任何 synthetic成员类嵌套在外(顶级)private类中,例如来自T,您可以使用T1。请注意,这也适用于new T2().t2成员。

JDK 1.1版中引入了 private static 属性,以支持嵌套类,这是当时java中的一种新语言功能。从那时起, JLS 明确允许对顶级类中的所有成员进行相互访问,包括synthetic个。

但是为了向后兼容,编译器会打开嵌套类(例如privateT$T1T$T2)并翻译T$T3成员访问 调用以生成 private 访问者方法(这些方法因此需要将包私有,即默认 em>,visibility(可见性):

synthetic

N.B。:您不能直接引用 class T { private int t; T() { // generated super(); // new Object() } static synthetic int access$t(T t) { // generated return t.t; } } class T$T1 { private int t1; final synthetic T t; // generated T$T1(T t) { // generated this.t = t; super(); // new Object() } static synthetic int access$t1(T$T1 t$t1) { // generated return t$t1.t1; } } class T$T2 extends T$T1 { private int t2; { System.out.println(T.access$t((T) this.t)); // t System.out.println(T$T1.access$t1((T$T1) this)); // super.t1 System.out.println(this.t2); } final synthetic T t; // generated T$T2(T t) { // generated this.t = t; super(this.t); // new T1(t) } static synthetic int access$t2(T$T2 t$t2) { // generated return t$t2.t2; } } class T$T3 extends T$T2 { { System.out.println(T.access$t((T) this.t)); // t System.out.println(T$T1.access$t1((T$T1) this)); // ((T1) this).t1 System.out.println(T$T2.access$t2((T$T2) this)); // super.t2 } final synthetic T t; // generated T$T3(T t) { // generated this.t = t; super(this.t); // new T2(t) } } 成员,因此在源代码中您无法使用,例如synthetic你自己。

答案 2 :(得分:7)

非常好的发现!我想,我们都假设您的代码示例应该编译。

不幸的是,事实并非如此......而JLS§15.11.2. "Accessing Superclass Members using super"给我们答案(强调我的):

  

假设字段访问表达式super.f出现在C类中,并且C的 immediate 超类是S类。如果S中的f可以从C类(第6.6节)访问,那么超级.f被视为在类S的主体中是表达式this.f.否则,发生编译时错误。

提供辅助功能,因为所有字段都在同一个封闭类中。它们可以是私人的,但仍然可以访问。

问题在于return serviceA(args) .or(() -> serviceB(args)) .or(() -> serviceC(args)); T2立即超类)将T3视为super.t1是非法的 - 有this.t1中没有字段t1。因此编译错误。