2009-10-21 19 views
7

El compilador de eclipse se niega a compilar el siguiente código, indicando que el campo s no está visible. (El compilador Aspect J de IBM también se niega, afirmando que "no se pudo resolver") ¿Por qué?rareza del compilador de Java: campo declarado en la misma clase, pero "no visible"

public class Test { 

    String s; 

    void foo(Object o) { 
     String os = getClass().cast(o).s; 
    } 
} 

La especificación del lenguaje Java afirma:

De lo contrario, nos dicen que no hay defecto acceso, lo cual está permitido sólo cuando el acceso se produce desde dentro del paquete de en la que se declara el tipo.

De la forma en que lo entiendo, el campo se declara y se accede en la misma unidad de compilación, por lo tanto, dentro del mismo paquete, y debe por lo tanto ser accesible.

Aún más extraño, la adición de un abatido ? extends Test-Test hace que el campo visible, es decir, el código siguiente se compila:

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 mal entendido el Spec Java?

Editar: Ahora estoy en otra computadora. Aquí, javac acepta el código, pero eclipse aún no lo hace. Versiones de esta máquina:

plataforma Eclipse

Versión: 3.4.2 Build ID: 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 usa el compilador Ascpect J de IBM ...

+0

problema de kewl. ¿Dónde está Josh Bloch? –

Respuesta

6

Prueba esto:

void foo(Object o) { 
    Test foo = getClass().cast(o); 
    String so = foo.s; 
} 

[Editar para aclarar]:

getClass().cast(o) devuelve un objeto de tipo 'capture#1-of? extends Test' y no Test. Entonces, el problema está relacionado con los genéricos y cómo lo trata el compilador. No conozco los detalles de la especificación en genéricos, pero dado que algunos compiladores (por comentarios aquí) aceptan su código, entonces esto es un agujero en el bucle en la especificación o algunos de estos compiladores no son completamente de acuerdo con las especificaciones.

[Últimas opiniones]: Creo que el compilador de eclipse es (cuidadosamente) correcto aquí. El objeto o puede de hecho ser una extensión de Prueba (y definido en otro paquete) y el compilador no tiene manera de saber si ese es realmente el caso o no. Por lo tanto, lo está tratando como el peor caso de una instancia de una extensión definida en otro paquete. Hubiera sido súper correcto si agregar un calificador final a la clase Test hubiera permitido el acceso al campo s, pero no es así.

+0

Tenga en cuenta que lo anterior se compiló en Mac OS X/Eclipse 3.4. – alphazero

+0

Erm ... ¿Ya lo hice? ¿O qué logra cambiar el nombre de la variable local de mi segundo fragmento de código? Sé que funciona Mi pregunta es por qué el primer fragmento no. – meriton

+0

No lo hace porque está asumiendo que la captura genérica también está definida en el mismo paquete por el compilador. Si no es así, la visibilidad del nivel del paquete ya no se aplica. – alphazero

2

No puedo reproducir lo que dice. Ambos compilan bien para mí sin advertencia, error ni nada con javac directamente.

WinXP, javac 1.6.0_16


No he intentado con Eclipse (v3.4.1, construir Identificación: M20080911-1700) y para el primero que dice:

The field Test.s is not visible 

En menos para Compiler Compliance level 1.6 y 1.5. Lo curioso es que, si nos fijamos en las opciones de solución rápida, enumera una resolución de Change to 's'. Lo cual, por supuesto, no resuelve el problema. Por lo que el compilador de Eclipse y la solución rápida "generador" parecen tener diferentes puntos de vista sobre esto también ;-)


Para el nivel de cumplimiento del compilador 1.4 (como era de esperar) en Eclipse para el primero que llegue

s cannot be resolved or is not a field 

y para el segundo recibo

Type mismatch: cannot convert from Object to Test 

Si especifico -source 1.4 y target -1.4 en la línea de comandos directamente javac dice que el primero

cannot find symbol 

y para el segundo uno obtengo

incompatible types 
+0

+1. Compila en 1.5 también. – ChssPly76

+0

Ahora estoy en otra computadora, donde javac acepta el código, pero eclipse todavía no lo hace. ¿Qué dice tu eclipse? Voy a repetir mi prueba en la máquina original mañana para asegurarme de haber probado correctamente. – meriton

4

Bien, veamos. Yo diría que el compilador no puede garantizar que foo() sea llamado por alguna entidad dentro del paquete, y por lo tanto no puede garantizar que s esté visible. Por ejemplo, agregue

protected void bar() { 
    foo(); 
} 

y luego de alguna subclase Bananaen otro paquete

public void quux() { bar(); } 

y vaya! getClass() produce Banana, que no puede ver s.

Edit: En cierto sentido, other.package.Banana no tiene un campo s. Si Banana fuera en el mismo paquete, aún podría tener su propia propiedad s, y debería hacer referencia a Test 's s a través de super.

+0

Según tengo entendido: que Banana no puede ver s significa que el código en la unidad de compilación banana no puede acceder a s. Pero mi código está en la unidad de compilación Prueba ... – meriton

+3

Sí, su código está en la unidad de compilación Prueba, pero una subclase putativa podría no serlo, y esa clase será el resultado de getClass(), y esa clase no puede ver el paquete -campo protegido El error del compilador es correcto, creo. –

1

Actualmente, en casi todos los casos, excepto cuando es requerido por Generics, es mejor (y más seguro) usar el operador de conversión de Java. Lo discutí here. El operador de Java Cast revisa detalladamente, pero aquí es la herramienta correcta.

Reemplazando el método cast con el operador se compila muy bien en Eclipse.

public class Test { 

    String s; 

    void foo(Object o) { 
     String os = ((Test) o).s; 
    } 
} 

creo que es correcto alphazerohere, que Eclipse es un poco más cauteloso.

0

Muy raro. Por una razón desconocida (para mí), el compilador 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 yeso con el JDK de Sun (estoy usando la versión 1.6.0_16 en GNU/Linux).

Cuestiones relacionadas