Différences de déballage automatique entre Java 6 et Java 7

107

J'ai noté une différence dans le comportement de déballage automatique entre Java SE 6 et Java SE 7. Je me demande pourquoi, car je ne trouve aucune documentation sur les changements de ce comportement entre ces deux versions.

Voici un exemple simple:

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

Cela compile bien avec javac de Java SE 7. Cependant, si je donne au compilateur l'argument "-source 1.6", j'obtiens une erreur sur la dernière ligne:

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

J'ai essayé de télécharger Java SE 6 pour compiler avec le compilateur natif de la version 6 (sans aucune option -source). Il accepte et donne la même erreur que ci-dessus.

Alors qu'est-ce qui donne? À partir d'un peu plus d'expérimentation, il semble que le déballage dans Java 6 ne peut déballer que des valeurs qui clairement (au moment de la compilation) sont du type encadré. Par exemple, cela fonctionne dans les deux versions:

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

Il semble donc qu'entre Java 6 et 7, la fonctionnalité de déballage a été améliorée afin de pouvoir convertir et déballer des types d'objets en un seul coup, sans savoir (au moment de la compilation) que la valeur est du type encadré approprié. Cependant, en lisant la spécification du langage Java ou les articles de blog qui ont été écrits au moment de la sortie de Java 7, je ne vois aucun changement de cette chose, donc je me demande quel est le changement et comment cette "fonctionnalité" est appelée ?

Juste une curiosité: en raison du changement, il est possible de déclencher de "faux" déballages:

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

Cela compile bien mais donne une exception ClassCastException au moment de l'exécution.

Une référence à ce sujet?

Morty
la source
17
Intéressant. Un nouvel ingrédient pour le désordre de l'autobox. Je pense que votre exemple pourrait être plus simple et plus clair avec un seul objet au lieu d'un tableau. Integer obj = new Integer(2); int x = (int)obj;: fonctionne sur Java 7, donne une erreur sur Java 6.
leonbloy
1
Quel JDK utilisez-vous? Cela pourrait aussi avoir à voir avec différents fournisseurs ...
barfuin
1
@leonbloy: Bon point sur la simplification, je l'ai un peu simplifié (à partir de mon code d'origine) mais je me suis arrêté trop tôt!
Morty
@Thomas: C'est le dernier JDK (pour chaque version) d'Oracle que j'ai utilisé.
Morty
2
Une autre raison de ne jamais utiliser la boxe automatique.
gyorgyabraham

Réponses:

92

Il semble que le langage de la section 5.5 Conversion de cast de Java 7 JLS a été mis à jour par rapport à la même section de Java 5/6 JLS , probablement pour clarifier les conversions autorisées.

Java 7 JLS dit

Une expression d'un type de référence peut subir une conversion de conversion en un type primitif sans erreur, par conversion de déballage.

Java 5/6:

Une valeur d'un type référence peut être convertie en un type primitif par conversion de déballage (§5.1.8).

Le JLS Java 7 contient également un tableau (tableau 5.1) des conversions autorisées (ce tableau n'est pas inclus dans le JLS Java 5/6) des types de référence aux primitives. Cela répertorie explicitement les transtypages d'objets en primitives comme une conversion de référence restrictive avec unboxing.

La raison est expliquée dans cet e-mail :

Bottom line: Si la spécification. allow (Object) (int) il doit également être autorisant (int) (Object).

Mark Rotteveel
la source
35

Vous avez raison; pour le dire plus simplement:

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

Cela fonctionne dans Java 7, mais donne une erreur de compilation dans Java 6 et inférieur. Curieusement, cette fonctionnalité n'est pas bien documentée; par exemple, ce n'est pas mentionné ici . Il est discutable s'il s'agit d'une nouvelle fonctionnalité ou d'un correctif de bogue (ou d'un nouveau bogue?), Voir quelques informations et discussions connexes . Le consensus semble indiquer une ambiguïté dans la spécification d'origine, ce qui a conduit à une implémentation légèrement incorrecte / incohérente sur Java 5/6, qui a été corrigée dans 7, car elle était essentielle pour l'implémentation de JSR 292 (Dynamically Typed Languages).

L'autoboxing Java a maintenant quelques pièges et surprises supplémentaires. Par exemple

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

compilera, mais échouera (avec ClassCastException) au moment de l'exécution. Cela fonctionnera à la place:

long x = (long)(int)obj;

Leonbloy
la source
2
Merci d'avoir répondu. Cependant, il y a une chose que je ne comprends pas. Il s'agit d'une clarification du JLS et des implémentations qui l'accompagnent (cf. la discussion par mail), mais pourquoi cela serait-il fait pour accueillir d'autres langages typés sur la JVM? Après tout, c'est un changement de langage, pas de machine virtuelle: le comportement de conversion de la machine virtuelle fonctionne comme toujours, le compilateur implémente cette fonctionnalité en utilisant le mécanisme existant pour la conversion en Integer et l'appel .intValue (). Alors, comment ce changement dans le langage Java proprement dit pourrait-il aider à exécuter d'autres langages sur la VM? Je suis d'accord que votre lien le suggère, je me demande simplement.
Morty