usar - javadoc wiki
¿Por qué pueden las clases secundarias anidadas acceder a miembros privados de su clase principal, pero los nietos no pueden? (3)
Probablemente similar a la pregunta, ¿Por qué pueden las clases Java externas acceder a miembros privados de clase interna? o Acceda a los campos privados de superclase utilizando la palabra clave super en una subclase .
Pero hay algunas diferencias: la clase de niños puede acceder a los miembros privados de su clase de padres (y solo al padre más cercano ).
Dado el código de muestra a continuación:
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
}
}
}
Métodos de acceso sintético
Técnicamente, en el nivel de JVM , NO se puede acceder a ningún miembro private
de otra clase, ni a los de una clase T2.t2
( Tt
), ni a los de una clase primaria ( T2.t2
). En su código, parece que puede hacerlo, porque el compilador genera métodos de acceso synthetic
para usted en las clases a las que accedió. Lo mismo sucede cuando en la clase T3
arreglas la referencia no válida con la forma correcta super.t1
((T1) this).t1
.
Con la ayuda de tal método de acceso synthetic
generado por el compilador, en general puede acceder a cualquier miembro private
de cualquier clase anidada en la clase T
externa (nivel superior), por ejemplo, desde T1
puede usar la new T2().t2
. Tenga en cuenta que esto también se aplica a private static
miembros private static
.
El atributo synthetic
se introdujo en la versión 1.1 de JDK para admitir clases anidadas, una nueva característica de lenguaje en java en ese momento. Desde entonces, el JLS permite explícitamente el acceso mutuo a todos los miembros dentro de una clase de nivel superior, incluidos los private
.
Pero para la compatibilidad con versiones anteriores, el compilador deshace las clases anidadas (por ejemplo, a T$T1
, T$T2
, T$T3
) y traduce private
accesos de los miembros private
a las llamadas a métodos de acceso synthetic
generados (por lo tanto, estos métodos deben tener el paquete privado , es decir, predeterminado). , visibilidad):
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)
}
}
NB: no se le permite referirse a miembros synthetic
directamente, por lo que en el código fuente no puede usar, por ejemplo, int i = T.access$t(new T());
tú mismo.
¡Ejemplo inteligente! Pero en realidad es una explicación un tanto aburrida: no hay problema de visibilidad, simplemente no hay manera de referirse a t1
directamente desde T3
porque super.super
no está permitido .
T2
no puede acceder a su propio campo t1
directamente ya que es privado (y las clases secundarias no heredan los campos privados de sus padres), pero super
es efectivamente una instancia de T1
y, dado que está en la misma clase, T2
puede referirse a los campos privados de super
Simplemente, no hay ningún mecanismo para que T3
dirija directamente a los campos privados de su clase de abuelos T1
.
Ambos compilan bien dentro de T3
, lo que demuestra que un T3
puede acceder a private
campos private
sus abuelos:
System.out.println(((T1)this).t1);
System.out.println(new T1().t1);
A la inversa, esto no se compila ni en T2
ni en T3
:
System.out.println(t1);
Si se permitiera super.super
, podrías hacer esto desde T3
:
System.out.println(super.super.t1);
si definiera 3 clases,
A
,B
,C
,A
con un campo protegidot1
yB
heredaría deA
yC
deB
,C
podría referirse aA
st1
invocandosuper.t1
porque es visible aquí. lógicamente, ¿no debería aplicarse lo mismo a la herencia de clases internas incluso si el campo es privado, porque estos miembros privados deberían ser visibles debido a que están en la misma clase?
(Voy a seguir con los nombres de las clases T1
, T2
y T3
OP por simplicidad)
Si t1
estuviera protected
no habría ningún problema: T3
podría referirse al campo t1
directamente como cualquier subclase. El problema se presenta con private
porque una clase no tiene conocimiento de private
campos private
de sus clases principales y, por lo tanto, no puede hacer referencia a ellos directamente, aunque en la práctica son visibles. Es por eso que tiene que usar super.t1
de T2
, incluso para referirse al campo en cuestión.
Aunque en lo que respecta a un T3
no tiene un campo t1
, tiene acceso a private
campos private
T1
al estar en la misma clase externa. Como ese es el caso, todo lo que necesita hacer es enviar this
a un T1
y tiene una forma de referirse al campo privado. La llamada super.t1
en T2
está (en esencia) enviando this
a un T1
que nos permite referirnos a sus campos.
Muy buen hallazgo! Creo que, todos habíamos asumido que su ejemplo de código debería compilar.
Desafortunadamente, no es el caso ... y el JLS nos da una respuesta en §15.11.2. "Accediendo a los miembros de Superclass usando super" (énfasis mío):
Supongamos que una expresión de acceso de campo super.f aparece dentro de la clase C, y la superclase inmediata de C es la clase S. Si se puede acceder a f en S desde la clase C (§6.6), entonces la superf se trata como si hubiera sido el expresión this.f en el cuerpo de la clase S. De lo contrario, se produce un error en tiempo de compilación.
Se da accesibilidad porque todos los campos están en la misma clase adjunta. Pueden ser privadas pero siguen siendo accesibles.
El problema es que en T2
(la superclase inmediata de T3
) el tratamiento de super.t1
ya que this.t1
es ilegal, no hay campo t1
en T2
. De ahí el error del compilador.