Probablement similaire à la question, Pourquoi les classes externes Java accèdent-elles aux membres privés de la classe interne? ou Accès aux champs privés de la superclasse en utilisant le mot clé super dans une sous-classe .
Mais il y a quelques différences: la classe enfants peut accéder aux membres privés de leur parent (et uniquement la classe parent la plus proche ).
Étant donné l'exemple de code ci-dessous:
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
}
}
}
Exemple intelligent! Mais c'est en fait une explication quelque peu ennuyeuse - il n'y a pas de problème de visibilité, vous n'avez tout simplement aucun moyen de vous référer à t1
directement de T3
car super.super
n'est pas autorisé .
T2
ne peut pas accéder à son propre t1
champ directement car il est privé (et les classes enfants n'héritent pas des champs privés de leurs parents), mais super
est en fait une instance de T1
et puisque c'est dans la même classe T2
peut faire référence aux champs privés de super
. Il n'y a tout simplement pas de mécanisme pour T3
pour adresser les champs privés de sa classe de grands-parents T1
directement.
Les deux compilent très bien à l'intérieur T3
, ce qui démontre qu'un T3
peut accéder aux champs private
de ses grands-parents:
System.out.println(((T1)this).t1);
System.out.println(new T1().t1);
Inversement, cela ne se compile ni dans T2
ou T3
:
System.out.println(t1);
Si super.super
ont été autorisés à le faire à partir de T3
:
System.out.println(super.super.t1);
si je définissais 3 classes,
A
,B
,C
,A
ayant un champ protégét1
etB
hériteraient deA
etC
deB
,C
pourraient faire référence àA
st1
en appelantsuper.t1
car il est visible ici. logiquement, la même chose ne devrait-elle pas s'appliquer à l'héritage des classes internes même si le champ est privé, car ces membres privés devraient être visibles car ils sont dans la même classe?
(Je vais m'en tenir à l'OP T1
, T2
, et T3
noms de classe pour plus de simplicité)
Si t1
étaient protected
il n'y aurait aucun problème - T3
pourrait faire référence à t1
champ directement comme n'importe quelle sous-classe. Le problème se pose avec private
car une classe n'a pas conscience des champs private
de ses classes parentes et peut donc 'ne les référencez pas directement, même si en pratique ils sont visibles. C'est pourquoi vous devez utiliser super.t1
de T2
, pour se référer même au domaine en question.
Même si jusqu'à T3
craint de ne pas avoir t1
champ auquel il a accès T1
s private
champs en étant dans la même classe externe. Puisque c'est le cas, tout ce que vous devez faire est de convertir this
en T1
et vous avez un moyen de vous référer au champ privé. Le super.t1
appelle T2
transforme (essentiellement) this
en T1
en nous référant à ses domaines.
Techniquement, au niveau [~ # ~] jvm [~ # ~], vous pouvez [~ # ~] pas [~ # ~] accéder aux private
membres d'une autre classe - ni ceux d'une classe englobante (T.t
), ni ceux d'une classe parente (T2.t2
). Dans votre code, il ressemble à vous pouvez, car le compilateur génère synthetic
méthodes d'accesseur pour vous dans les classes accédées. La même chose se produit lorsque dans la classe T3
Vous corrigez la référence non valide en utilisant le bon formulaire super.t1
((T1) this).t1
.
Avec l'aide d'un tel compilateur généré synthetic
méthode d'accesseur, vous pouvez en général accéder à private
membre de toute classe imbriquée dans la couche externe (niveau supérieur) T
classe, par exemple à partir de T1
, vous pouvez utiliser new T2().t2
. Notez que cela s'applique également aux membres private static
.
L'attribut synthetic
a été introduit dans [~ # ~] jdk [~ # ~] 1.1 pour prendre en charge les classes imbriquées, une nouvelle fonctionnalité de langage dans Java à cette époque. Depuis lors, le [~ # ~] jls [~ # ~] permet explicitement accès mutuel à tous les membres d'une classe de niveau supérieur, y compris private
ceux.
Mais pour des raisons de compatibilité ascendante, le compilateur déballe les classes imbriquées (par exemple en T$T1
, T$T2
, T$T3
) Et traduit private
membre accède aux appels aux appels générés synthetic
méthodes d'accesseur (ces méthodes doivent donc avoir le package privé, c'est-à-dire par défaut, visibilité):
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 .: Vous n'êtes pas autorisé à vous référer directement aux membres synthetic
, donc dans le code source, vous ne pouvez pas utiliser par exemple int i = T.access$t(new T());
vous-même.
Très bonne trouvaille! Je pense que nous avions tous supposé que votre exemple de code devrait être compilé.
Malheureusement, ce n'est pas le cas ... et le JLS nous donne une réponse dans §15.11.2. "Accès aux membres de la superclasse en utilisant super " (c'est moi qui souligne):
Supposons qu'une expression d'accès au champ super.f apparaît dans la classe C, et la immédiate superclasse de C est la classe S. Si f dans S est accessible depuis la classe C (§6.6), alors super.f est traité comme s'il s'agissait de l'expression this.f dans le corps de la classe S. Sinon, une erreur de compilation se produit.
L'accessibilité est donnée car tous les champs sont dans la même classe englobante. Ils peuvent être privés mais restent accessibles.
Le problème est que dans T2
(la immédiate superclasse de T3
) le traitement de super.t1
comme this.t1
est illégal - il n'y a pas de champ t1
dans T2
. D'où l'erreur du compilateur.