为什么嵌套的 child classes 可以访问其 parent class 的私有成员,而 grandchildren 不能?
Why can nested child classes access private members of their parent class, but grandchildren cannot?
可能与问题类似,Why can outer Java classes access inner class private members? or Access to superclass private fields using the super keyword in a subclass .
但也有一些区别:children class 可以访问其 parent 的私有成员(而 只能访问最近的 parent) class.
给出下面的示例代码:
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
isn't allowed.
T2
不能直接访问它自己的 t1
字段,因为它是私有的(并且 child classes 不继承它们的 parent' s 私有字段),但 super
实际上是 T1
的一个实例,并且由于它在同一个 class 中,T2
可以引用 super
的私有字段。 T3
没有机制直接处理其 grandparent class T1
的私有字段。
这两个在 T3
中编译得很好,这表明 T3
可以访问其 grandparent 的 private
字段:
System.out.println(((T1)this).t1);
System.out.println(new T1().t1);
相反,这不会在 T2
或 T3
中编译:
System.out.println(t1);
如果允许 super.super
,您可以从 T3
:
执行此操作
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 A
s 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 的 T1
、T2
和 T3
class 名称)
如果 t1
是 protected
就没有问题 - T3
可以像任何子 class 一样直接引用 t1
字段。 private
出现问题是因为 class 没有 awareness 其 parent classes 的 private
字段,因此不能直接引用它们,即使在实践中它们是可见的。这就是为什么您必须使用 T2
中的 super.t1
,以便甚至引用相关字段。
即使就 T3
而言,它没有 t1
字段,但它可以访问 T1
s private
字段,方法是位于相同的外部 class。既然是这种情况,您需要做的就是将 this
转换为 T1
并且您有办法引用私有字段。 T2
中的 super.t1
调用(本质上)将 this
转换为 T1
让我们引用它的字段。
很好的发现!我想,我们都假设您的代码示例应该可以编译。
不幸的是,情况并非如此...... JLS gives us an answer in §15.11.2. "Accessing Superclass Members using super"(强调我的):
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.
提供了可访问性,因为所有字段都在同一个封闭 class 中。它们可以是私有的,但仍然可以访问。
问题是在T2
中(T3
的立即超class)将super.t1
处理为this.t1
是非法的 - T2
中没有字段 t1
。因此编译器错误。
综合访问器方法
从技术上讲,在 JVM 级别上,您可以 NOT 访问另一个 class 的任何 private
成员— 既不是封闭的 class (T.t
),也不是父 class (T2.t2
) 的那些。在您的代码中,它 看起来像 您可以,因为编译器会在访问的 class 中为您生成 synthetic
访问器方法。当在 T3
class 中使用正确的形式 ((T1) this).t1
.[=34= 修复无效引用 super.t1
时,也会发生同样的情况]
借助编译器生成的synthetic
访问器方法,一般可以访问anyprivate
any class 的成员嵌套在外部(顶层)T
class,例如从 T1
开始,您可以使用 new T2().t2
。请注意,这也适用于 private static
成员。
synthetic
属性在 JDK 版本 1.1 中引入以支持嵌套 class es,当时java的一个新的语言特性。从那时起,JLS 明确允许在顶级 class 内的所有成员相互访问,包括 private
成员。
但为了向后兼容,编译器解开嵌套的 classes(例如 T$T1
、T$T2
、T$T3
)并翻译 private
成员 accesses to calls to generated synthetic
accessor methods(因此这些方法需要有 包私有,即默认,可见性):
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)
}
}
N.B.: 你不能直接引用 synthetic
成员,所以在源代码中你不能使用例如int i = T.access$t(new T());
你自己。
可能与问题类似,Why can outer Java classes access inner class private members? or Access to superclass private fields using the super keyword in a subclass .
但也有一些区别:children class 可以访问其 parent 的私有成员(而 只能访问最近的 parent) class.
给出下面的示例代码:
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
isn't allowed.
T2
不能直接访问它自己的 t1
字段,因为它是私有的(并且 child classes 不继承它们的 parent' s 私有字段),但 super
实际上是 T1
的一个实例,并且由于它在同一个 class 中,T2
可以引用 super
的私有字段。 T3
没有机制直接处理其 grandparent class T1
的私有字段。
这两个在 T3
中编译得很好,这表明 T3
可以访问其 grandparent 的 private
字段:
System.out.println(((T1)this).t1);
System.out.println(new T1().t1);
相反,这不会在 T2
或 T3
中编译:
System.out.println(t1);
如果允许 super.super
,您可以从 T3
:
System.out.println(super.super.t1);
if I'd define 3 classes,
A
,B
,C
,A
having a protected fieldt1
andB
would inherit fromA
andC
fromB
,C
could refer toA
st1
by invokingsuper.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 的 T1
、T2
和 T3
class 名称)
如果 t1
是 protected
就没有问题 - T3
可以像任何子 class 一样直接引用 t1
字段。 private
出现问题是因为 class 没有 awareness 其 parent classes 的 private
字段,因此不能直接引用它们,即使在实践中它们是可见的。这就是为什么您必须使用 T2
中的 super.t1
,以便甚至引用相关字段。
即使就 T3
而言,它没有 t1
字段,但它可以访问 T1
s private
字段,方法是位于相同的外部 class。既然是这种情况,您需要做的就是将 this
转换为 T1
并且您有办法引用私有字段。 T2
中的 super.t1
调用(本质上)将 this
转换为 T1
让我们引用它的字段。
很好的发现!我想,我们都假设您的代码示例应该可以编译。
不幸的是,情况并非如此...... JLS gives us an answer in §15.11.2. "Accessing Superclass Members using super"(强调我的):
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.
提供了可访问性,因为所有字段都在同一个封闭 class 中。它们可以是私有的,但仍然可以访问。
问题是在T2
中(T3
的立即超class)将super.t1
处理为this.t1
是非法的 - T2
中没有字段 t1
。因此编译器错误。
综合访问器方法
从技术上讲,在 JVM 级别上,您可以 NOT 访问另一个 class 的任何 private
成员— 既不是封闭的 class (T.t
),也不是父 class (T2.t2
) 的那些。在您的代码中,它 看起来像 您可以,因为编译器会在访问的 class 中为您生成 synthetic
访问器方法。当在 T3
class 中使用正确的形式 ((T1) this).t1
.[=34= 修复无效引用 时,也会发生同样的情况]
super.t1
借助编译器生成的synthetic
访问器方法,一般可以访问anyprivate
any class 的成员嵌套在外部(顶层)T
class,例如从 T1
开始,您可以使用 new T2().t2
。请注意,这也适用于 private static
成员。
synthetic
属性在 JDK 版本 1.1 中引入以支持嵌套 class es,当时java的一个新的语言特性。从那时起,JLS 明确允许在顶级 class 内的所有成员相互访问,包括 private
成员。
但为了向后兼容,编译器解开嵌套的 classes(例如 T$T1
、T$T2
、T$T3
)并翻译 private
成员 accesses to calls to generated synthetic
accessor methods(因此这些方法需要有 包私有,即默认,可见性):
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)
}
}
N.B.: 你不能直接引用 synthetic
成员,所以在源代码中你不能使用例如int i = T.access$t(new T());
你自己。