rareza del compilador java: campo declarado en la misma clase, pero "no visible"
-
05-07-2019 - |
Pregunta
El compilador de eclipse se niega a compilar el siguiente código, indicando que el campo s no es visible. (El compilador Aspect J de IBM también se niega, declarando que " s no se pudo resolver ") ¿Por qué?
public class Test {
String s;
void foo(Object o) {
String os = getClass().cast(o).s;
}
}
Los estados de la especificación del lenguaje Java:
De lo contrario, decimos que hay por defecto acceso, que está permitido sólo cuando el acceso se produce desde dentro de la Paquete en el que se declara el tipo.
De la forma en que lo entiendo, el campo se declara y se accede a él en la misma unidad de compilación, por lo tanto, dentro del mismo paquete, y por lo tanto debería estar accesible.
Aún más extraño, ¿agregar un downcast desde ? extiende Test
a Test
hace que el campo esté visible, es decir, que el siguiente código compile:
public class Test {
String s;
void foo(Object o) {
Test t = getClass().cast(o);
String os = t.s;
}
}
¿He tropezado con un error del compilador o no he entendido bien las especificaciones de Java?
Editar: Estoy en otra computadora ahora. Aquí, javac acepta el código, pero eclipse todavía no lo hace. Versiones en esta máquina:
Plataforma Eclipse
Versión: 3.4.2 ID de compilación: M20090211-1700
JDK 1.6.0
Editar 2 De hecho, javac acepta el código. Lo había probado ejecutando la compilación ant, que utiliza el compilador Ascpect J de IBM ...
Solución
Prueba esto:
void foo(Object o) {
Test foo = getClass().cast(o);
String so = foo.s;
}
[Editar para aclarar]:
getClass (). cast (o)
devuelve un objeto del tipo ' capture # 1-of? extiende Test
'y no Test
. Entonces, el problema está relacionado con los genéricos y cómo el compilador lo trata. No conozco los detalles de la especificación sobre genéricos, pero dado que algunos compiladores (de acuerdo con los comentarios que aparecen aquí) aceptan su código, este es un agujero de bucle en la especificación o algunos de estos compiladores no están completamente de acuerdo con la especificación. / p>
[Últimos pensamientos]:
Creo que el compilador de eclipse es en realidad (con cuidado) correcto aquí. El objeto o
puede de hecho ser una extensión de Test (y definido en otro paquete) y el compilador no tiene forma de saber si ese es realmente el caso o no. Por lo tanto, lo trata como el peor caso de una instancia de una extensión definida en otro paquete. Habría sido súper correcto si agregar un calificador final
a la clase Prueba
hubiera permitido el acceso al campo s
, pero no lo hace.
Otros consejos
Bueno, veamos. Diría que el compilador no puede garantizar correctamente que alguna entidad dentro del paquete llamará a foo ()
y, por lo tanto, no puede garantizar que s
esté visible. Por ejemplo, añadir
protected void bar() {
foo();
}
y luego en alguna subclase Banana
en otro paquete
public void quux() { bar(); }
y oops! getClass ()
produce Banana
, que no puede ver s
.
Editar: En cierto sentido, other.package.Banana no tiene un campo s
. Si Banana estuviera en el mismo paquete, aún podría tener su propia propiedad s
, y tendría que referirse a la Prueba
de s
a través de super
.
No puedo reproducir lo que estás diciendo. Ambos se compilan bien para mí sin advertencia, error o cualquier cosa con javac directamente.
WinXP, javac 1.6.0_16
No lo intenté con eclipse (v3.4.1, ID de compilación: M20080911-1700) y para el primero dice:
The field Test.s is not visible
Al menos para el nivel de conformidad del compilador 1.6 y 1.5.
Lo gracioso es que, si nos fijamos en las opciones de Arreglo rápido, enumera una resolución de Cambiar a 's'
. Lo que por supuesto no resuelve el problema. Por lo tanto, el compilador de eclipse y el generador Quick-fix " " parece que tienen diferentes puntos de vista sobre esto también ;-)
Para el nivel de conformidad del compilador 1.4 (como era de esperar) en eclipse para el primero que recibo
s cannot be resolved or is not a field
y para la segunda me sale
Type mismatch: cannot convert from Object to Test
Si especifico -source 1.4
y target -1.4
en la línea de comando directamente javac
dice que para el primero
cannot find symbol
y para la segunda me sale
incompatible types
En realidad, en casi todos los casos, excepto cuando los Genéricos lo requieren, es mejor (y más seguro) usar el operador de Java. Lo comenté aquí . El operador de cast de Java se ve más detallado, pero es la herramienta correcta aquí.
Reemplazar el método cast
con el operador compila perfectamente en Eclipse.
public class Test {
String s;
void foo(Object o) {
String os = ((Test) o).s;
}
}
Creo que alphazero es correcto aquí , que Eclipse es demasiado cauteloso.
Muy raro. Por una razón desconocida (para mí), el compilador de eclipse requiere una conversión explícita:
void foo(Object o) {
String os = ((Test)getClass().cast(o)).s;
}
Mientras que el código se compila perfectamente sin el elenco con JDK de Sun (estoy ejecutando la versión 1.6.0_16 en GNU / Linux).