Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

But there are some differences: the children class can access the private members of their parent (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         }     } } 
like image 869
andyf Avatar asked Jul 25 '16 05:07

andyf


People also ask

Can nested classes access private members?

A nested class is a member of its enclosing class. Non-static nested classes (inner classes) have access to other members of the enclosing class, even if they are declared private.

Can child class access private method of parent?

There is absolute no way this can be done. You cannot call a private method of a class from any other class(be it the extending class). It can only be accessed inside that class itself.

Can a subclass access the private members of its parent?

A subclass does not inherit the private members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass. A nested class has access to all the private members of its enclosing class—both fields and methods.

Can a parent class access the public members of child class how?

We can access the public, protected and private members of parent class in sub classes/child class. But public class can access the protected and public members of child class as well.


2 Answers

Clever example! But it's actually a somewhat boring explanation - there's no visibility problem, you simply have no way of referring to t1 directly from T3 because super.super isn't allowed.

T2 can't access its own t1 field directly since it's private (and child classes don't inherit their parent's private fields), but super is effectively an instance of T1 and since it's in the same class T2 can refer to the private fields of super. There's just no mechanism for T3 to address the private fields of its grandparent class T1 directly.

Both of these compile just fine inside T3, which demonstrates that a T3 can access its grandparent's private fields:

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

Conversely this doesn't compile in either T2 or T3:

System.out.println(t1); 

If super.super were allowed you'd be able to do this from 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 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?

(I'm going to stick with OP's T1, T2, and T3 class names for simplicity)

If t1 were protected there'd be no problem - T3 could refer to the t1 field directly just like any subclass. The issue arises with private because a class has no awareness of its parent classes' private fields, and therefore can't reference them directly, even though in practice they are visible. That's why you have to use super.t1 from T2, in order to even refer to the field in question.

Even though as far as a T3 is concerned it has no t1 field it has access into T1s private fields by being in the same outer class. Since that's the case all you need to do is cast this to a T1 and you have a way to refer to the private field. The super.t1 call in T2 is (in essence) casting this into a T1 letting us refer to its fields.

like image 62
dimo414 Avatar answered Sep 22 '22 19:09

dimo414


Sythetic Accessor Methods

Technically, on the JVM level, you can NOT access any private members of another class — neither those of an enclosing class (T.t), nor those of a parent class (T2.t2). In your code it just looks like you can, because the compiler generates synthetic accessor methods for you in the accessed classes. The same happens when in the T3 class you fix the invalid reference super.t1 using the correct form ((T1) this).t1.

With the help of such a compiler generated synthetic accessor method, you can in general access any private member of any class nested in the outer (top level) T class, e.g. from T1 you can use new T2().t2. Note that this applies to private static members too.

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

But for backwards compatibility, the compiler unwraps nested classes (e.g. to T$T1, T$T2, T$T3) and translates private member accesses to calls to generated synthetic accessor methods (these methods thus need to have the package private, i.e. default, visibility):

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.: You're not allowed to refer to synthetic members directly, so in the source code you can't use e.g. int i = T.access$t(new T()); yourself.

like image 23
charlie Avatar answered Sep 21 '22 19:09

charlie