Casting de Java: ¿Está mal el compilador, o la especificación del idioma está mal o estoy equivocado?

StackOverflow https://stackoverflow.com/questions/5385743

  •  28-10-2019
  •  | 
  •  

Pregunta

He estado leyendo la especificación del idioma Java, tercera edición, y he encontrado lo que creo que es una discrepancia entre la especificación y la implementación del compilador Javac. Existen las mismas discrepancias en el compilador Eclipse.

Sección 15.16 Habla sobre expresiones de reparto. Dice que debería ser un error de tiempo de compilación si el tipo de argumento no se puede convertir al tipo de reparto mediante la conversión de fundición (Sección 5.5):

Es un error de tiempo de compilación si el tipo de tiempo de compilación del operando nunca puede ser lanzado al tipo especificado por el operador de reparto de acuerdo con las reglas de conversión de lanzamiento (§5.5). De lo contrario, en el tiempo de ejecución, el valor del operando se convierte (si es necesario) mediante la conversión de la conversión al tipo especificado por el operador de fundición.

Sección 5.5 Habla sobre la conversión de casting. Da una lista de tipos de conversión permitidos. Específicamente ausente de la lista está la "conversión de unboxing seguida de ampliar/reducir la conversión primitiva". Sin embargo Esa secuencia exacta de conversiones parece estar permitida por el compilador Javac (y también el compilador de eclipse). Por ejemplo:

long l = (long) Integer.valueOf(45);

... se compila bien. (El elenco problemático es el elenco para long; El argumento es de tipo java.lang.Integer, por lo que la conversión requiere unboxing a int seguido de una conversión primitiva amplia).

Del mismo modo, según el JLS, no debería ser posible emitir byte a char, porque eso (según 5.1.4) requiere una conversión primitiva amplia y Una conversión primitiva estrecha, sin embargo, los compiladores también permiten este elenco.

¿Alguien puede iluminarme?

Editar: Desde que pregunté esto, he presentado un informe de error con Oracle. Su respuesta es que esta es una "falla en el JLS".

¿Fue útil?

Solución

Creo que tienes razón, los compiladores tienen razón y la especificación es incorrecta ...

Esto compila: (Object)45 Y esto no: (Long)45

La única forma de dar sentido al comportamiento de los compiladores (incluido el IntelliJ que estoy usando) es si la conversión de lanzamiento se modifica para estar de acuerdo con la conversión de la tarea y la conversión de invocación del método:

  • Una conversión de boxeo (§5.1.7) opcionalmente seguida de una conversión de referencia amplia

  • Una conversión de unboxing (§5.1.8) opcionalmente seguida de una conversión primitiva amplia.

más

  • ensanchando y estrechando la convesión primitiva

La especificación dijo que "las conversiones de lanzamiento son más inclusivas que las conversiones de asignación o invocación de métodos: un elenco puede hacer cualquier conversión permitida que no sea una conversión de cadena o una conversión de captura"

Otros consejos

Por mi lectura, el elenco de int a long está permitido por esta cláusula:

Se puede lanzar un valor de un tipo primitivo a otro tipo primitivo mediante la conversión de identidad, si los tipos son los mismos, o mediante una conversión primitiva amplia o una conversión primitiva estrecha.

Mudado int a long es un Conversión primitiva ampliante.

Que simplemente deja la conversión de Integer a int, que se adapta a la última bala:

una conversión de unboxing

Por supuesto, el elenco a long Ni siquiera es necesario en el ejemplo.

Considere las siguientes cuatro definiciones:

final Integer io = Integer.valueOf(45);
final int i = io;
final long l1 = (long)i;
final long l2 = i;

¿Considera que alguno de ellos sorprendente? Su ejemplo original no se ve diferente; Simplemente elide las variables intermedias.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top