Perché non è possibile che il compilatore / JVM solo fare autoboxing “solo lavoro”?
-
25-09-2019 - |
Domanda
Autoboxing è piuttosto spaventoso. Pur comprendendo pienamente la differenza tra ==
e .equals
non posso non aiuto avere il bug follow l'inferno fuori di me:
final List<Integer> foo = Arrays.asList(1, 1000);
final List<Integer> bar = Arrays.asList(1, 1000);
System.out.println(foo.get(0) == bar.get(0));
System.out.println(foo.get(1) == bar.get(1));
che stampa
true
false
Perché l'hanno fatto in questo modo? E 'qualcosa a che fare con i numeri interi nella cache, ma se questo è il caso perché non hanno semplicemente memorizza nella cache tutti gli interi utilizzati dal programma? Oppure, perché non fa la JVM sempre auto Unbox di primitiva?
Stampa di falso falso o vero vero modo sarebbe stato meglio.
Modifica
Non sono d'accordo per rottura del vecchio codice. Avendo ritorno foo.get(0) == bar.get(0)
vero che già rotto il codice.
Può non può risolvere il problema a livello compilatore sostituendo numeri interi con int nel codice byte (fintanto che non viene mai assegnato null)
Soluzione
- Perché lo fanno in questo modo?
Ogni numero intero compreso tra -128 e 127 viene memorizzato nella cache da Java. Lo hanno fatto, presumibilmente, per il miglioramento delle prestazioni. Anche se volevano tornare indietro su questa decisione adesso, è improbabile che lo farebbero. Se qualcuno codice costruito in funzione di questo, il loro codice romperebbe quando è stato tolto. Per la codifica il mio hobby, questo forse non importa, ma per il codice aziendale, le persone si arrabbiano e cause legali accadere.
- Perché non solo cache di tutti gli interi utilizzati dal programma?
tutti gli interi non può essere memorizzata nella cache, perché le implicazioni di memoria sarebbe enorme.
- Perché l'JVM sempre auto Unbox di primitiva?
Perché la JVM non può sapere quello che si voleva. Inoltre, questo cambiamento potrebbe facilmente rompere il codice legacy non costruito per gestire questo caso.
Se la JVM per automaticamente unboxing alle primitive sulle chiamate a ==, la questione sarà effettivamente diventare più confusa. Ora è necessario ricordare che == confronta sempre riferimenti a oggetti, a meno che gli oggetti possono essere unboxed. Ciò causerebbe ancora più strana confusione casi proprio come quello che di cui sopra.
Invece di preoccuparsi troppo duro su questo, basta ricordare questa regola, invece:
Mai oggetti confrontare con == a meno che non si intende essere confrontandoli con i loro riferimenti. Se lo fai, non riesco a pensare ad uno scenario in cui ci si esegue in un problema.
Altri suggerimenti
Potete immaginare come prestazioni male sarebbe se ogni Integer
portato in testa per l'internamento? non lo fa funzionare anche per new Integer
.
Il linguaggio Java (JVM non è un problema) non può sempre automaticamente Unbox perché codice progettato per il pre-1.5 Java dovrebbe funzionare.
Integer
s nel intervallo di byte sono lo stesso oggetto, perché sono memorizzati nella cache. Integer
s fuori dell'intervallo di byte non sono. Se tutti i numeri interi dovevano essere memorizzati nella cache, immaginare la memoria richiesta.
E da qui
Il risultato di tutta questa magia è che si può in gran parte ignorare la distinzione tra int e Integer, con alcune precisazioni. Un'espressione intera può avere un valore nullo. Se il programma tenta di autounbox nulla, si getterà una NullPointerException. I confronti di identità esegue operatore == di riferimento sulle espressioni intere e confronti di uguaglianza valore su Int espressioni. Infine, ci sono i costi di prestazioni associati con la boxe e unboxing, anche se viene fatto automaticamente
Se si salta autoboxing completamente, è ancora ottenere questo comportamento.
final List<Integer> foo =
Arrays.asList(Integer.valueOf( 1 ), Integer.valueOf( 1000 ));
final List<Integer> bar =
Arrays.asList(Integer.valueOf( 1 ), Integer.valueOf( 1000 ));
System.out.println(foo.get(0) == bar.get(0)); // true
System.out.println(foo.get(1) == bar.get(1)); // false
essere più espliciti, se si desidera un comportamento specifico:
final List<Integer> foo =
Arrays.asList( new Integer( 1 ), new Integer( 1000 ));
final List<Integer> bar =
Arrays.asList( new Integer( 1 ), new Integer( 1000 ));
System.out.println(foo.get(0) == bar.get(0)); // false
System.out.println(foo.get(1) == bar.get(1)); // false
Questa è una ragione, perché Eclipse ha autoboxing come un avvertimento per impostazione predefinita.
Un sacco di persone hanno problemi con questo problema, anche le persone che i libri di scrittura su Java.
Nel Pro di programmazione Java , meri pollici al di sotto sono stati i autore parla problemi con l'utilizzo interi auto-inscatolato come una chiave in un'IdentityHashMap, usa le chiavi di auto-boxed intero in un WeakHashMap. I valori di esempio che usa sono maggiore di 128, quindi la sua chiamata garbage collection riesce. Se qualcuno dovesse usare il suo esempio e sull'uso di valori più piccolo di 128, però, il suo esempio fallirebbe (a causa della chiave è perma-cache).
Quando si scrive
foo.get(0)
il compilatore non importa quanto si è creato l'elenco. Si guarda solo il tipo di tempo di compilazione della lista foo. Quindi, se questo è un List
System.out.println(foo.get(0).intValue() == bar.get(0).intValue());
non
System.out.println(foo.get(0) == bar.get(0));
, perché questo ha un significato completamente diverso.