Differenze nell'unboxing automatico tra Java 6 e Java 7


107

Ho notato una differenza nel comportamento di unboxing automatico tra Java SE 6 e Java SE 7. Mi chiedo perché ciò sia, perché non riesco a trovare alcuna documentazione delle modifiche in questo comportamento tra queste due versioni.

Ecco un semplice esempio:

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

Questo si compila bene con javac da Java SE 7. Tuttavia, se do al compilatore l'argomento "-source 1.6" ottengo un errore nell'ultima riga:

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

Ho provato a scaricare Java SE 6 per compilare con il compilatore versione 6 nativa (senza alcuna opzione -source). Accetta e restituisce lo stesso errore di cui sopra.

Allora cosa succede? Da qualche ulteriore sperimentazione sembra che l'unboxing in Java 6 possa solo unboxing di valori che chiaramente (in fase di compilazione) è del tipo boxed. Ad esempio, funziona in entrambe le versioni:

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

Quindi sembra che tra Java 6 e 7, la funzione di unboxing sia stata migliorata in modo che potesse lanciare e decomprimere i tipi di oggetti in un colpo solo, senza sapere (in fase di compilazione) che il valore è del tipo boxed corretto. Tuttavia, leggendo le specifiche del linguaggio Java o i post sul blog scritti al momento in cui è uscito Java 7, non riesco a vedere alcun cambiamento di questa cosa, quindi mi chiedo quale sia il cambiamento e come si chiama questa "funzionalità" ?

Solo una curiosità: a causa del cambiamento, è possibile attivare unboxing "sbagliato":

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

Questo si compila bene ma fornisce un'eccezione ClassCastException in fase di esecuzione.

Qualche riferimento su questo?


17
Interessante. Un nuovo ingrediente per il pasticcio dell'autoboxing. Penso che il tuo esempio potrebbe essere più semplice e chiaro con un singolo oggetto invece di un array. Integer obj = new Integer(2); int x = (int)obj;: funziona su Java 7, dà errore su Java 6.
Leonbloy

1
Quale JDK stai usando? Potrebbe anche avere a che fare con diversi fornitori ...
barfuin

1
@leonbloy: Buon punto sulla semplificazione, l'ho semplificata un po '(dal mio codice originale) ma in qualche modo mi sono fermato troppo presto!
Morty

@Thomas: era l'ultimo JDK (per ogni versione) di Oracle che ho usato.
Morty

2
Un altro motivo per non usare mai l'autoboxing.
Gyorgyabraham

Risposte:


92

Sembra che la lingua nella sezione 5.5 Conversione del casting di Java 7 JLS sia stata aggiornata rispetto alla stessa sezione in Java 5/6 JLS , probabilmente per chiarire le conversioni consentite.

Java 7 JLS dice

Un'espressione di un tipo di riferimento può subire la conversione del casting in un tipo primitivo senza errori, mediante conversione unboxing.

Java 5/6:

Un valore di un tipo di riferimento può essere convertito in un tipo primitivo mediante la conversione unboxing (§5.1.8).

Java 7 JLS contiene anche una tabella (tabella 5.1) delle conversioni consentite (questa tabella non è inclusa in Java 5/6 JLS) dai tipi di riferimento alle primitive. Questo elenca esplicitamente i cast da Object a primitive come una conversione di riferimento restrittiva con unboxing.

Il motivo è spiegato in questa email :

Conclusione: se le specifiche. allow (Object) (int) deve anche essere allow (int) (Object).


35

Hai ragione; per dirla più semplicemente:

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

Funziona in Java 7, ma fornisce un errore di compilazione in Java 6 e inferiore. Stranamente, questa caratteristica non è ben documentata; ad esempio, non è menzionato qui . È discutibile se si tratta di una nuova funzionalità o di una correzione di bug (o di un nuovo bug?), Vedere alcune informazioni e discussioni correlate . Il consenso sembra indicare un'ambiguità nelle specifiche originali, che ha portato a un'implementazione leggermente errata / incoerente su Java 5/6, che è stata corretta in 7, perché era fondamentale per l'implementazione di JSR 292 (Dynamically Typed Languages).

L'autoboxing di Java ha ora alcune trappole e sorprese in più. Per esempio

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

compilerà, ma fallirà (con ClassCastException) in fase di esecuzione. Questo, invece, funzionerà:

long x = (long)(int)obj;


2
Grazie per la risposta. Tuttavia, c'è una cosa che non capisco. Questo è un chiarimento del JLS e delle implementazioni di accompagnamento (cfr. La discussione per posta), ma perché dovrebbe essere fatto per accogliere altri linguaggi tipizzati sulla JVM? Dopotutto, è una modifica alla lingua, non alla VM: il comportamento di casting della VM funziona come sempre, il compilatore implementa questa funzionalità usando il meccanismo esistente per il casting su Integer e chiamando .intValue (). Quindi come potrebbe questo cambiamento nel linguaggio Java corretto, aiutare a eseguire altri linguaggi sulla VM? Sono d'accordo che il tuo link lo suggerisca, mi chiedo solo.
Morty
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.