java - tipos - No se puede acceder a la variable privada desde la propia clase a través de la instancia de subclase
palabras reservadas para herencia en java (7)
class A {
private int foo;
void bar(B b) { b.foo = 42; }
}
class B extends A { }
Esto no se compila con el error:
A.java:3: error: foo has private access in A
void bar(B b) { b.foo = 42; }
^
1 error
Agregar un cast a la clase base lo hace funcionar.
void bar(B b) { ((A) b).foo = 42; }
¿Alguien puede indicarme una explicación sobre por qué el primer fragmento es ilegal? ¿Cuál es la razón por la que está prohibido? Esto es lo que dice el JLS :
De lo contrario, el miembro o el constructor se declara
private
, y el acceso está permitido si y solo si ocurre dentro del cuerpo de la clase de nivel superior (§7.6) que encierra la declaración del miembro o constructor.
Lo mejor que puedo decir, mi código cumple con esta redacción. Entonces, ¿esto es un error con el compilador de Java o mi interpretación de JLS es incorrecta?
(Nota: no estoy buscando soluciones, como hacer que la variable esté protected
. Sé cómo solucionar esto).
¿No es esto para lo que es el modificador de acceso predeterminado?
Prueba esto :
public class blah{
static class A {
int foo;
void bar(B b) {b.foo=42;}
}
static class B extends A {
}
}
No puede acceder al miembro privado directamente desde un antepasado, eso es lo que significa privado. Ahora, ¿por qué funciona cuando lanzas? ¿Y eso significa que la documentación es incorrecta?
Le mencioné a un colega que la documentación de java puede ser errónea y él señala que en realidad está configurando el valor de foo dentro de la clase A. Así que todo es correcto. No puedes (porque es privado) acceder a foo desde un descendiente, por lo que debes lanzar. Y no puedes hacer eso fuera del cuerpo de A.
Creo que esta es la respuesta correcta.
El mensaje de error "tiene un acceso privado en A" es un error de Java durante mucho tiempo.
JDK 1.1:
JDK-4096353 : JLS 6.6.1: cuando se usan referencias de subclases para acceder a datos privados de superclases
contiene un fragmento de código que se ajusta exactamente a la pregunta uno
class X{
private static int i = 10;
void f() {
Y oy = new Y();
oy.i = 5; // Is this an error? Is i accessable through a reference to Y?
}
}
class Y extends X {}
Intentaron arreglarlo y lleva a
JDK-4122297 : los mensajes de error de javac no son apropiados para un campo privado.
======TP1======
1 class C extends S {
2 void f(){
3 java.lang.System.out.println("foo");
4 }
5 }
6
7 class S {
8 private int java;
9 }
======
% javac C.java
C.java:3: Variable java in class S not accessible from class C.
java.lang.System.out.println("foo");
^
C.java:3: Attempt to reference field lang in a int.
java.lang.System.out.println("foo");
^
2 errors
======
Pero según la especificación, java
no se hereda en C y este programa debería compilar.
Se fijó en 1.2, pero vuelve a aparecer en 1.3.
JDK-4240480 : name00705.html: los miembros privados JLS6.3 no deben heredarse de las superclases
JDK-4249653 : el nuevo javac asume que los campos privados son heredados por una subclase
Y cuando vienen los genéricos.
JDK-6246814 : miembro privado de tipo variable accesible incorrectamente
JDK-7022052 : Error de compilador no válido en métodos privados y genéricos
Sin embargo, por el JLS este miembro simplemente no existe en el tipo heredado.
JLS 8.2. Miembros de la clase
Los miembros de una clase que se declaran privados no son heredados por las subclases de esa clase.
Entonces b.foo
es ilegal porque la clase B
no tiene un campo llamado foo
. No es ninguna restricción, es un campo ausente en B
Java tiene una escritura fuerte y no podemos acceder a campos que no existen en B
incluso si existen en la superclase A
El reparto (A) b
es legal porque B
es una subclase de A
A
tiene un campo llamado foo
y podemos acceder a este campo privado porque b(B b)
es una función en la clase A
incluso si b != this
debido a
JLS 6.6.1. Determinando la accesibilidad
De lo contrario, si el miembro o el constructor se declara privado, entonces se permite el acceso si y solo si ocurre dentro del cuerpo de la clase de nivel superior (§7.6) que encierra la declaración del miembro o constructor.
Tambien si escribimos
class A {
private int foo;
void baz(A b) { b.foo = 42; }
}
class B extends A { }
class T {
void x() {
B b = new B();
b.baz(b);
}
}
Se compilará porque Java infiere argumentos de tipo para llamadas polimórficas.
JLS 15.12.2.7. Inferir argumentos de tipo basados en argumentos reales:
Una restricción de supertipo T:> X implica que la solución es uno de los supertipos de X. Dadas varias de estas restricciones en T, podemos intersecar los conjuntos de supertipos implicados por cada una de las restricciones, ya que el parámetro type debe ser un miembro de todos ellos. Luego podemos elegir el tipo más específico que se encuentra en la intersección.
Java es delicado al acceder a variables privadas a través de un tipo de referencia que no debería tener acceso a esa variable. Debe poder hacer esto legalmente escribiendo ((A) b).foo = 42
.
La especificación para expresiones de acceso a campos, capítulo 15.11 dice:
Si el identificador no nombra un campo miembro accesible en el tipo T, entonces el acceso al campo no está definido y se produce un error en tiempo de compilación.
Desde la perspectiva de la súper clase, mirando los tipos, diría que el miembro no es accesible , de ahí el error.
Creo que el caso que está presentando está más cerca de acceder a un miembro como un campo, que se muestra en el ejemplo 15.11-1-1.
class S { int x = 0; }
class T extends S { int x = 1; }
class Test1 {
public static void main(String[] args) {
T t = new T();
System.out.println("t.x=" + t.x + when("t", t));
S s = new S();
System.out.println("s.x=" + s.x + when("s", s));
s = t;
System.out.println("s.x=" + s.x + when("s", s));
}
static String when(String name, Object t) {
return " when " + name + " holds a "
+ t.getClass() + " at run time.";
}
}
Sólo para responder a su pregunta:
Por favor explique qué tipo de código malo protege contra la restricción.
Por favor considere el siguiente fragmento.
public class X {
private int a;
public void bar(Z z) {
z.a // not visible, but if was, what ''a''
// would you actually access at this point ''X'' or ''Z''
}
}
public class Z extends X {
private int a;
}
Me parece que la especificación es inconsistente. Como dice John, el cuerpo de los estados espec.
De lo contrario, el miembro o el constructor se declara privado, y el acceso está permitido si y solo si ocurre dentro del cuerpo de la clase de nivel superior (§7.6) que encierra la declaración del miembro o constructor.
y no hay mención de subclases. Así que la clase A debería compilar correctamente. Sin embargo, el ejemplo 6.6-5 estados
Se puede acceder a un miembro o constructor de clase privada solo dentro del cuerpo de la clase de nivel superior (§7.6) que encierra la declaración del miembro o constructor. No es heredado por subclases .
Esta segunda declaración es más débil (no solo si), pero trae subclases a la tabla. De acuerdo con esto, A no debe compilar.
No podemos heredar los campos o métodos private
. Por lo tanto, en su código, la Clase B
ignora por completo la variable foo
, aunque esté accediendo desde su propia Clase A
No puede decir b.foo
porque foo
es privado y, por lo tanto, no se heredará, como resultado, la clase B
no puede ver la variable foo
y no sabe si existe una variable llamada foo
, a menos que esté marcada como protegida (como usted dijo) o predeterminado (ya que están en el mismo paquete, supongo) o público.
Si desea usar foo
sin usar una this.foo
explícita como su segundo ejemplo, debe usar this.foo
o simplemente foo
que tiene this
implícito. Como Javadocs especificó, la razón principal de this
palabra clave es evitar que:
La razón más común para usar esta palabra clave es porque un campo está sombreado por un método o un parámetro constructor.
Cuando usó ((A) b)
está emitiendo el tipo de referencia y el compilador lo verá como si estuviera usando un tipo de variable de referencia A
, en otras palabras, algo como A a
, y a.foo
es completamente legal.
Un resumen ilustrado de la visibilidad y el acceso a las variables de instancia privada de la superclase: