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)
相反,这不会编译在任何一个T2或T3:
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会继承A和C从B,C可能是指A小号t1援引super.t1这里,因为it's可见.逻辑上应该不适用于内部类继承,即使该字段是私有的,因为这些私有成员应该是可见的,因为它们在同一个类中?
(为了简单起见T1,我将坚持使用OP T2,和T3类名称)
如果t1是protected有好多是没有问题的- T3可能是指在t1现场直接就像任何的子类.出现这个问题是private因为一个类没有意识到它的父类的private字段,因此不能直接引用它们,即使在实践中它们是可见的.这就是为什么你必须使用super.t1from T2,以便甚至引用相关领域.
尽管就a T3而言它没有t1字段,它可以通过在同一个外部类中访问T1s private字段.既然如此,您需要做的就是强制转换this为a,T1并且您有办法引用私有字段.该super.t1呼叫T2是(本质)铸造this成一个T1让我们引用它的领域.
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());自己.
非常好的发现!我想,我们都假设您的代码示例应该编译.
不幸的是,情况并非如此...... JLS在§15.11.2中给出了答案."使用super访问超类成员"(强调我的):
假设字段访问表达式super.f出现在类C中,并且C的直接超类是类S.如果S中的f可以从类C(第6.6节)访问,则super.f被视为如果它是在类S的主体中表达this.f.否则,发生编译时错误.
给出了可访问性,因为所有字段都在同一个封闭类中.它们可以是私人的,但仍然可以访问.
问题是,在T2(对直接超类T3)的治疗super.t1为this.t1是非法的-没有任何领域t1中T2.因此编译错误.