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

and*_*dyf 50 java visibility inner-classes

可能类似于问题,为什么外部Java类可以访问内部类私有成员?使用子类中的super关键字访问超类私有字段.

但是存在一些差异:子类可以访问其父级(并且只有最近的父级)的私有成员.

鉴于以下示例代码:

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

dim*_*414 50

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

T2不能t1直接访问自己的字段,因为它是私有的(并且子类不继承其父级的私有字段),但super实际上是一个实例,T1因为它在同一个类T2中可以引用它的私有字段super.没有直接T3解决其祖父母类的私有字段的机制T1.

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

System.out.println(((T1)this).t1);
System.out.println(new T1().t1);
Run Code Online (Sandbox Code Playgroud)

相反,这不会编译在任何一个T2T3:

System.out.println(t1);
Run Code Online (Sandbox Code Playgroud)

如果super.super被允许,您可以通过以下方式执行此操作T3:

System.out.println(super.super.t1);
Run Code Online (Sandbox Code Playgroud)

如果我定义3个班,A,B,C,A有保护地t1,并B会继承ACB,C可能是指A小号t1援引super.t1这里,因为it's可见.逻辑上应该不适用于内部类继承,即使该字段是私有的,因为这些私有成员应该是可见的,因为它们在同一个类中?

(为了简单起见T1,我将坚持使用OP T2,和T3类名称)

如果t1protected有好多是没有问题的- T3可能是指在t1现场直接就像任何的子类.出现这个问题是private因为一个类没有意识到它的父类的private字段,因此不能直接引用它们,即使在实践中它们是可见的.这就是为什么你必须使用super.t1from T2,以便甚至引用相关领域.

尽管就a T3而言它没有t1字段,它可以通过在同一个外部类中访问T1s private字段.既然如此,您需要做的就是强制转换this为a,T1并且您有办法引用私有字段.该super.t1呼叫T2是(本质)铸造this成一个T1让我们引用它的领域.

  • 所有这些类都可以访问彼此的`private`字段,因为它们都在同一个外层. (9认同)
  • @KevinEsche不同之处在于:声明为`protected`的成员是_inherited_,而声称为'private`的成员则不是_inherited_.这意味着字段`protected int t1`也是`B`(或示例中的`T2`)的成员,因此允许在`C`(或`T3`)中使用`super.t2`进行访问. (2认同)

cha*_*lie 15

合成访问器方法

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

在这种编译器生成的synthetic访问器方法的帮助下,您可以一般访问嵌套在外层(顶层)类中的任何类的任何 private成员,例如从您可以使用.请注意,这也适用于成员.TT1new T2().t2private static

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

但为了向后兼容,编译器解开嵌套类(例如,到T$T1,T$T2,T$T3)和平移private构件访问的呼叫到所产生synthetic的存取方法(因此这些方法需要有包专用,即默认,可见性):

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:您不允许synthetic直接引用成员,因此在源代码中您不能使用例如您int i = T.access$t(new T());自己.


See*_*ose 7

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

不幸的是,情况并非如此...... JLS§15.11.2中给出了答案."使用super访问超类成员"(强调我的):

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

给出了可访问性,因为所有字段都在同一个封闭类中.它们可以是私人的,但仍然可以访问.

问题是,在T2(对直接超类T3)的治疗super.t1this.t1是非法的-没有任何领域t1T2.因此编译错误.