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

Probably similar to the question Why can Java outer classes access private members of an inner class? or Access private fields of a superclass using the super keyword in a subclass .

But there are some differences: the children class can access the private members of its parent class (and only the nearest parent class ).

Given the sample code below:

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 } } } 
+48
java visibility inner-classes
Jul 25 '16 at 5:41
source share
3 answers

Clever example! But this is actually a somewhat boring explanation - there is no visibility problem, you just do not have the ability to refer to t1 directly from T3 , because super.super not allowed .

T2 cannot access its own field t1 directly, since it is private (and child classes do not inherit their parent private fields), but super actually an instance of t1 , and since it can refer to private fields super in the same class T2 . There is simply no mechanism for T3 to directly access the private fields of its grandparent t1 class.

Both of these are compiled just thin inside T3 , which demonstrates that T3 can access grandparent private fields:

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

Conversely, this does not compile in either T2 or T3 :

 System.out.println(t1); 

If super.super was allowed, you can do it from T3 :

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



if I defined 3 classes, A , B , C , A with the protected field t1 and B would inherit from A and C from B , C could refer to A t1 , calling super.t1 , because it is visible here. should not logically coincide with the inheritance of inner classes, even if the field is private, because these private members must be visible due to the fact that they are in the same class?

(I just want to use the OP class names t1 , T2 and T3 )

If t1 were protected , there would be no problem - T3 could refer to the field t1 just like any subclass. The problem arises with private , because the class does not know the fields of the parent classes private and therefore cannot refer to them directly, although in practice they are visible. This is why you should use super.t1 from T2 to even reference the corresponding field.

Despite the fact that with respect to T3 it does not have the t1 field, it has access to the t1 private fields, being in the same external class. Since all you have to do is make this to t1 , and you have a way to access the private field. Calling super.t1 in T2 is (essentially) casting this to t1 , allowing us to reference its fields.

+48
Jul 25 '16 at 5:51
source share
โ€” -

Synthetic Access Methods

Technically, at the JVM level, you can NOT access any private members of another class - not members of the class ( Tt ), but not the class of the parent class ( T2.t2 ). In your code, it just looks like it , because the compiler generates synthetic access methods for you in the access classes. The same thing happens when you correct the invalid super.t1 link in the T3 class using the correct form ((T1) this).t1 .

Using such a compiler, the generated synthetic access method, you can publicly access any private member of any class nested in the external (upper level) class T , for example from T1 you can use new T2().t2 . Note that this also applies to private static members.

The synthetic attribute was introduced in version 1.1 of the JDK to support nested classes, a new language function in java at the time. Since then, JLS explicitly allows mutual access to all members of the top-level class, including private ones.

But for backward compatibility, the compiler expands the nested classes (for example, up to T$T1 , T$T2 , T$T3 ) and translates the private member calls the generated synthetic access methods (thus, these methods must have a closed package, i.e. value default, visibility):

 class T { private int t; T() { // generated super(); // new Object() } static synthetic int access$t(T t) { // generated return tt; } } 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) } } 

NB: You are not allowed to directly access synthetic members, so you cannot use, for example, in the source code. int i = T.access$t(new T()); by yourself.

+13
Jul 25 '16 at 7:11
source share

Very nice find! I think we all suggested that your sample code should compile.

Unfortunately, this is not so ... and JLS gives us the answer in ยง15.11. 2. "Access to superclass members using super" (emphasis added):

Suppose that an expression of class super.f for a field is available in class C, and a superclass of class immediate C is class S. If f in S is accessible from class C (ยง6.6), then super .f is treated as if it were an expression this.f is in the body of class S. Otherwise, a compile-time error occurs.

Availability is specified because all fields are in the same class. They may be private, but still available.

The problem is that in T2 (the direct superclass of T3 ), treating super.t1 like this.t1 is illegal - there is no t1 field in T2 . Therefore, a compiler error.

+6
Jul 25 '16 at 5:56
source share



All Articles