Diferencias en el desempaquetado automático entre Java 6 y Java 7


107

He notado una diferencia en el comportamiento de desempaquetado automático entre Java SE 6 y Java SE 7. Me pregunto por qué es así, porque no puedo encontrar ninguna documentación de cambios en este comportamiento entre estas dos versiones.

He aquí un ejemplo sencillo:

Object[] objs = new Object[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];

Esto se compila bien con javac de Java SE 7. Sin embargo, si le doy al compilador el argumento "-source 1.6", aparece un error en la última línea:

inconvertible types
found   : java.lang.Object
required: int

Intenté descargar Java SE 6 para compilarlo con el compilador nativo de la versión 6 (sin ninguna opción de fuente). Está de acuerdo y da el mismo error que el anterior.

Entonces, ¿qué da? A partir de un poco más de experimentación, parece que el unboxing en Java 6 solo puede desempaquetar valores que claramente (en el momento de la compilación) son del tipo encuadrado. Por ejemplo, esto funciona en ambas versiones:

Integer[] objs = new Integer[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];

Entonces, parece que entre Java 6 y 7, la función de desempaquetado se mejoró para que pudiera lanzar y desempaquetar tipos de objetos de una sola vez, sin saber (en el momento de la compilación) que el valor es del tipo de caja correcto. Sin embargo, al leer la Especificación del lenguaje Java o las publicaciones de blogs que se escribieron en el momento en que salió Java 7, no veo ningún cambio en esto, así que me pregunto cuál es el cambio y cómo se llama esta "característica". ?

Solo una curiosidad: Debido al cambio, es posible desencadenar unboxings "incorrectos":

Object[] objs = new Float[2];
objs[0] = new Float(5);
int myInt = (int)objs[0];

Esto se compila bien pero da una ClassCastException en tiempo de ejecución.

¿Alguna referencia sobre esto?


17
Interesante. Un nuevo ingrediente para el lío del autoboxing. Creo que su ejemplo podría ser más simple y claro con un solo objeto en lugar de una matriz. Integer obj = new Integer(2); int x = (int)obj;: funciona en Java 7, da error en Java 6.
leonbloy

1
¿Qué JDK estás usando? También podría tener que ver con diferentes proveedores ...
barfuin

1
@leonbloy: Buen punto sobre la simplificación, lo simplifiqué un poco (de mi código original), ¡pero de alguna manera me detuve demasiado pronto!
Morty

@Thomas: Fue el último JDK (para cada versión) de Oracle que utilicé.
Morty

2
Otra razón para no utilizar nunca el autoboxing.
gyorgyabraham

Respuestas:


92

Parece que el lenguaje de la sección 5.5 Conversión de conversión de Java 7 JLS se actualizó en comparación con la misma sección en Java 5/6 JLS , probablemente para aclarar las conversiones permitidas.

Java 7 JLS dice

Una expresión de un tipo de referencia puede sufrir una conversión de conversión a un tipo primitivo sin error, mediante la conversión de unboxing.

Java 5/6:

Un valor de un tipo de referencia se puede convertir en un tipo primitivo mediante conversión de unboxing (§5.1.8).

Java 7 JLS también contiene una tabla (tabla 5.1) de conversiones permitidas (esta tabla no está incluida en Java 5/6 JLS) de tipos de referencia a primitivas. Esto enumera explícitamente las conversiones de Object a primitivas como una conversión de referencia restringida con unboxing.

El motivo se explica en este correo electrónico :

En pocas palabras: si la especificación. permite (Object) (int) también debe permitir (int) (Object).


35

Tienes razón; para decirlo más simplemente:

Object o = new Integer(1234);
int x = (int) o;

Esto funciona en Java 7, pero da un error de compilación en Java 6 y versiones anteriores. Curiosamente, esta característica no está documentada de forma destacada; por ejemplo, no se menciona aquí . Es discutible si se trata de una nueva función o una corrección de errores (¿o un nuevo error?), Consulte información y discusión relacionada . El consenso parece apuntar a una ambigüedad en la especificación original, lo que llevó a una implementación ligeramente incorrecta / inconsistente en Java 5/6, que se corrigió en 7, porque era fundamental para la implementación de JSR 292 (lenguajes dinámicamente tipados).

El autoboxing de Java tiene ahora más trampas y sorpresas. Por ejemplo

Object obj = new Integer(1234);
long x = (long)obj;

se compilará, pero fallará (con ClassCastException) en tiempo de ejecución. Esto, en cambio, funcionará:

long x = (long)(int)obj;


2
Gracias por la respuesta. Sin embargo, hay una cosa que no entiendo. Esta es una aclaración del JLS y las implementaciones que lo acompañan (cf. la discusión por correo), pero ¿por qué se haría eso para acomodar otros lenguajes escritos en el JVM? Después de todo, es un cambio en el lenguaje, no en la VM: el comportamiento de conversión de la VM funciona como siempre, el compilador implementa esta característica usando el mecanismo existente para transmitir a Integer y llamar a .intValue (). Entonces, ¿cómo podría este cambio en el lenguaje Java adecuado ayudar a ejecutar otros lenguajes en la máquina virtual? Estoy de acuerdo en que su enlace sugiere esto, solo me pregunto.
Morty
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.