usar new icon example codigo borderfactory java generics intellij-idea jshell

new - Genéricos de Java: ¿por qué es posible esta salida?



new icon java (4)

El comportamiento que muestra IntelliJ es claro para mí:

Tienes un elenco sin MyClass en MyClass . Esto significa que el new Integer(8) no se convierte inmediatamente en Long sino en el Number borrado (que funciona), cuando se ejecuta esta línea: N n =(N)(new Integer(8));

Ahora echemos un vistazo a las declaraciones de salida:

System.out.println(new MyClass<Long>().n);

se reduce a String.valueOf(new MyClass<Long>().n) -> ((Object)new MyClass<Long>().n).toString() que funciona bien, porque n se accede a través de Object y también el Se accede al método toString() a través del tipo estático Object -> no se produce Long a Long . new MyClass<Long>().n.toString() fallará con una excepción, porque se intenta acceder a toString() a través del tipo estático Long . Por lo tanto, se produce una conversión de n para escribir Long que no es posible ( Integer no se puede convertir a Long ).

Lo mismo ocurre al ejecutar la segunda declaración:

System.out.println(new MyClass<Long>().n.getClass());

Se getClass método getClass (declarado en Object ) de tipo Long a través del tipo estático Long . Por lo tanto, se produce una conversión de n para escribir Long que produce una excepción de conversión.

Comportamiento de JShell:

Traté de reproducir la excepción resultante para la primera declaración de salida en JShell - Java 9 Early Access Build 151:

jshell> class MyClass<N extends Number> { ...> N n = (N) (new Integer(8)); ...> } | Warning: | unchecked cast | required: N | found: java.lang.Integer | N n = (N) (new Integer(8)); | ^--------------^ | created class MyClass jshell> System.out.println(new MyClass<Long>().n); 8 jshell> System.out.println(new MyClass<Long>().n.getClass()); | java.lang.ClassCastException thrown: java.base/java.lang.Integer cannot be cast to java.base/java.lang.Long | at (#4:1)

Pero parece que JShell da exactamente los mismos resultados que IntelliJ. System.out.println(new MyClass<Long>().n); salidas 8 - sin excepción.

Tengo esta clase:

class MyClass<N extends Number> { N n = (N) (new Integer(8)); }

Y quiero obtener estos resultados:

System.out.println(new MyClass<Long>().n); System.out.println(new MyClass<Long>().n.getClass());

  1. Salida de la primera instrucción System.out.println() :

    8

  2. Salida de la segunda instrucción System.out.println() :

    java.lang.ClassCastException: java.lang.Integer (in module: java.base) cannot be cast to java.lang.Long (in module: java.base)

¿Por qué obtengo el primer resultado? ¿No hay un elenco también? ¿Por qué obtengo la excepción en la segunda salida?

PD: yo uso Java 9; Lo probé con JShell y obtuve una excepción en ambas salidas. Luego lo probé con IntelliJ IDE y obtuve el primer resultado, pero la excepción en el segundo.


Esto está sucediendo porque ya ha definido n como objeto de entero, por lo que no lo lanzará demasiado

use Integer en MyClass en sysout como

System.out.println(new MyClass<Integer>().n);

o defina n como: N n =(N)(new Long(8)); .


Esto sucede debido a la eliminación de Java.

Como Integer extiende Number , el compilador acepta la conversión a N En tiempo de ejecución, dado que N se reemplaza por Number (debido a la eliminación), no hay ningún problema para almacenar un número Integer dentro de n .

El argumento del método System.out.println es de tipo Object por lo que no hay problema para imprimir el valor de n .

Sin embargo, cuando se llama a un método en n , el compilador agrega una verificación de tipo para garantizar que se llame al método correcto. Por lo tanto, resulta en una ClassCastException .


Tanto la excepción como la no excepción son comportamientos permitidos. Básicamente, esto se reduce a cómo el compilador borra las declaraciones, ya sea para algo así sin lanzamientos:

System.out.println(new MyClass().n); System.out.println(new MyClass().n.getClass());

o algo parecido a esto con los moldes:

System.out.println((Long)new MyClass().n); System.out.println(((Long)new MyClass().n).getClass());

o uno para una declaración y uno para el otro. Ambas versiones son código Java válido que se compilará. La pregunta es si está permitido que el compilador compile en una versión, en la otra, o en ambas.

Es permisible insertar un reparto aquí porque eso es generalmente lo que sucede cuando tomas algo de un contexto genérico donde el tipo es una variable de tipo y lo devuelves a un contexto donde la variable de tipo adquiere un tipo específico. Por ejemplo, puede asignar el new MyClass<Long>().n a una variable de tipo Long sin ninguna conversión, o pasar el new MyClass<Long>().n a un lugar que espera Long sin ninguna conversión, en ambos casos obviamente requerirá que el compilador inserte un molde. El compilador puede decidir insertar siempre un reparto cuando tiene un new MyClass<Long>().n , y no está mal hacerlo, ya que se supone que la expresión tiene el tipo Long .

Por otro lado, también es permisible no tener una conversión en estas dos declaraciones, porque en ambos casos la expresión se usa en un contexto en el que se puede usar cualquier Object , por lo que no se necesita conversión para hacer que se compile y sea de tipo seguro. . Además, en ambas declaraciones, un reparto o ningún reparto no supondría ninguna diferencia en el comportamiento si el valor fuera realmente Long . En la primera instrucción, se pasa a la versión de .println() que toma Object , y no hay más sobrecarga específica de println que toma Long o Number o algo así, por lo que se elegirá la misma sobrecarga independientemente de si El argumento se considera Long u Object . Para la segunda instrucción, .getClass() es proporcionado por Object , por lo que está disponible independientemente de si la cosa de la izquierda es Long u Object . Dado que el código borrado es válido tanto con y sin el elenco y el comportamiento sería el mismo con y sin el elenco (suponiendo que la cosa sea realmente Long ), el compilador podría optar por optimizar el reparto.

Un compilador podría incluso tener un reparto en un caso y no en el otro, tal vez porque solo optimiza el reparto en ciertos casos simples, pero no se molesta en realizar análisis en casos más complicados. No necesitamos pensar en por qué un compilador en particular decidió compilar en un formulario u otro para una declaración en particular, porque ambos son permisibles, y no debe confiar en que funcione de una manera u otra.