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

Why can nested child classes access private members of their parent class, but grandchildren cannot?

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

但是有一些区别:子类可以访问其父类(并且只能访问最近的父类)的私有成员。

给出下面的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
        }
    }
}


聪明的例子!但这实际上是一个有点无聊的解释——没有能见度问题,你根本没有办法直接从T3提到t1,因为super.super是不允许的。

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

这两种方法都在T3中编译得很好,这表明T3可以访问其祖父母的private字段:

1
2
System.out.println(((T1)this).t1);
System.out.println(new T1().t1);

相反,这在T2T3中都不编译:

1
System.out.println(t1);

如果允许super.super的话,你可以从T3那里得到:

1
System.out.println(super.super.t1);

if I'd define 3 classes, A, B, C, A having a protected field t1 and B would inherit from A and C from B, C could refer to As t1 by invoking super.t1 because it′s visible here. logically shouldn't the same apply to inner classes inheritance even if the field are private, because these private members should be visible due to being in the same class?

(为了简单起见,我将继续使用OP的t1T2T3类名)

如果t1protected就没有问题了,T3可以像任何子类一样直接引用t1字段。这个问题出现在private中,因为一个类不知道它的父类的private字段,因此不能直接引用它们,即使在实践中它们是可见的。这就是为什么你必须使用来自T2super.t1,以便甚至参考相关领域。

即使对于一个T3而言,它没有t1字段,但它可以通过处于同一外部类进入t1private字段。既然是这样,你所需要做的就是把this强制转换成t1,你就有办法引用私有字段。T2中的super.t1调用实质上是把this转换成t1,让我们参考它的字段。


Sythetic访问方法

从技术上讲,在JVM级别上,您不能访问其他类的任何private成员;既不能访问封闭类(T.t)的成员,也不能访问父类(T2.t2)的成员。在您的代码中,看起来您可以这样做,因为编译器在访问的类中为您生成synthetic访问方法。在T3类中,使用正确的格式((T1) this).t1修复无效引用时,也会发生同样的情况。

借助于这种编译器生成的synthetic访问方法,一般可以访问嵌套在外部(顶层)T类中的任何类的任何private成员,例如从t1可以使用new T2().t2。请注意,这也适用于private static成员。

在JDK版本1.1中引入了EDCOX1的48属性,以支持嵌套类,在Java中是一种新的语言特征。从那时起,JLS明确允许顶级类中的所有成员相互访问,包括private个成员。

但为了向后兼容,编译器解包嵌套类(例如到T$T1T$T2T$T3,并将private成员访问转换为调用生成的synthetic访问器方法(因此这些方法需要包私有,即默认、可见性):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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成员,因此在源代码中不能使用,例如int i = T.access$t(new T());自己。


很好的发现!我认为,我们都假定您的代码示例应该编译。

不幸的是,事实并非如此……JLS在第15.11.2节中给出了答案。"使用super(emphasis mine)访问超类成员:

Suppose that a field access expression super.f appears within class C, and the immediate superclass of C is class S. If f in S is accessible from class C (§6.6), then super.f is treated as if it had been the expression this.f in the body of class S. Otherwise, a compile-time error occurs.

提供了可访问性,因为所有字段都在同一封闭类中。它们可以是私有的,但仍然可以访问。

问题是,在T2中(T3的直接超类),把super.t1当作this.t1处理是非法的——T2中没有字段t1。因此出现了编译器错误。