Autoboxing in Java
- L'autoboxing in Java è il processo automatico di incapsulamento di un tipo primitivo in un oggetto wrapper corrispondente.
- L'auto-unboxing è il processo automatico di estrazione del valore primitivo da un oggetto wrapper.
- L'autoboxing e l'auto-unboxing semplificano la codifica, eliminando la necessità di gestire manualmente il boxing e l'unboxing.
- L'autoboxing e l'auto-unboxing avvengono automaticamente quando si assegnano valori a variabili di tipo wrapper o si passano argomenti a metodi.
- L'autoboxing e l'auto-unboxing possono avvenire anche all'interno delle espressioni.
- L'autoboxing e l'auto-unboxing possono essere utilizzati con i tipi wrapper per
boolean
echar
. - L'autoboxing e l'auto-unboxing possono prevenire errori comuni associati al boxing e all'unboxing manuale.
- Si raccomanda di utilizzare l'autoboxing e l'auto-unboxing per semplificare il codice, ma si dovrebbe evitare di abusarne per non compromettere le prestazioni.
Autoboxing
Le versioni moderne di Java hanno incluso due caratteristiche importanti: autoboxing e auto-unboxing.
L'autoboxing è il processo mediante il quale un tipo primitivo viene automaticamente incapsulato (boxed) nel suo tipo wrapper equivalente ogni volta che è necessario un oggetto di quel tipo. Non è necessario costruire esplicitamente un oggetto. L'auto-unboxing è il processo mediante il quale il valore di un oggetto boxed viene automaticamente estratto (unboxed) da un tipo wrapper quando è necessario il suo valore. Non è necessario chiamare un metodo come intValue()
o doubleValue()
.
L'autoboxing e l'auto-unboxing semplificano notevolmente la codifica di diversi algoritmi, rimuovendo la noiosa necessità di fare manualmente il boxing e l'unboxing dei valori. Aiutano anche a prevenire errori. Inoltre, sono molto importanti per i generics, che operano solo su oggetti. Infine, l'autoboxing rende molto più facile lavorare con il Collections Framework. Generics e Collections Framework sono argomenti che tratteremo in dettaglio nelle prossime lezioni.
Con l'autoboxing, non è necessario costruire manualmente un oggetto per avvolgere un tipo primitivo. È sufficiente assegnare quel valore a un riferimento di tipo wrapper. Java costruisce automaticamente l'oggetto per noi. Ad esempio, ecco il modo moderno per costruire un oggetto Integer
che ha il valore 100:
// Autoboxing di un intero in Java
Integer iOgg = 100;
Notiamo che l'oggetto non viene esplicitamente inserito in un wrapper. Java se ne occupa automaticamente per noi.
Per fare l'unboxing di un oggetto, è sufficiente assegnare quel riferimento di oggetto a una variabile di tipo primitivo. Ad esempio, per fare l'unbox di iOgg
, possiamo usare questa riga di codice:
// Auto-unboxing di un Integer in un int
int i = iOgg;
Java gestisce i dettagli per noi.
Di seguito riportiamo un semplice programma di esempio:
// Dimostrazione dell'Autoboxing e Auto-unboxing in Java
class AutoBox {
public static void main(String[] args) {
// Autoboxing di un intero in Java
Integer iOgg = 100;
// Auto-unboxing di un Integer in un int
int i = iOgg;
System.out.println(i + " " + iOgg); // visualizza 100 100
}
}
Autoboxing e Metodi
Oltre al caso semplice delle assegnazioni, l'autoboxing avviene automaticamente ogni volta che un tipo primitivo deve essere convertito in un oggetto; l'auto-unboxing ha luogo ogni volta che un oggetto deve essere convertito in un tipo primitivo.
Pertanto, autoboxing/unboxing potrebbe verificarsi quando un argomento viene passato a un metodo, o quando un valore viene restituito da un metodo.
Ad esempio, consideriamo questo:
// Autoboxing/unboxing ha luogo con
// parametri di metodo e valori di ritorno.
class AutoBox2 {
// Prende un parametro Integer e restituisce
// un valore int;
static int m(Integer v) {
// Auto-unboxing di v
return v ;
}
public static void main(String[] args) {
// Passa un int a m() e assegna il valore di ritorno
// a un Integer. Qui, l'argomento 100 è autoboxed
// in un Integer. Il valore di ritorno è anche autoboxed
// in un Integer.
Integer iOb = m(100);
System.out.println(iOb);
}
}
Questo programma visualizza il seguente risultato:
100
Nel programma, notiamo che m()
specifica un parametro Integer
e restituisce un risultato int
. All'interno di main()
, a m()
viene passato il valore 100. Poiché m()
si aspetta un Integer
, questo valore viene automaticamente boxed. Poi, m()
restituisce l'equivalente int
del suo argomento. Questo causa l'auto-unboxing di v
. Successivamente, questo valore int
viene assegnato a iOb
in main()
, il che causa l'autoboxing del valore di ritorno int
.
Autoboxing e Auto-Unboxing nelle Espressioni
In generale, autoboxing e unboxing hanno luogo ogni volta che è richiesta una conversione in un oggetto o da un oggetto.
Questo si applica anche alle espressioni. All'interno di un'espressione, ad un oggetto numerico viene automaticamente applicato l'unboxing.
Al risultato dell'espressione viene applicato nuovamente il boxing, se necessario. Ad esempio, consideriamo il seguente programma:
// Autoboxing/unboxing all'interno delle espressioni.
class AutoBox3 {
public static void main(String[] args) {
// Due oggetti Integer
Integer iOggetto, iOggetto2;
// Un int primitivo
int i;
iOggetto = 100;
System.out.println("Valore originale di iOggetto: " + iOggetto);
// La seguente riga causa l'unboxing di iOggetto,
// l'incremento del suo valore, e poi il boxing
// del risultato di nuovo in iOggetto.
// Quindi, l'operatore ++ viene applicato al valore
// unboxed di iOggetto.
++iOggetto;
System.out.println("Dopo ++iOggetto: " + iOggetto);
// La seguente riga causa l'unboxing di iOggetto,
// l'esecuzione dell'espressione, e poi il boxing
// del risultato in iOggetto2.
// Quindi, l'operatore + viene applicato al valore
// unboxed di iOggetto.
iOggetto2 = iOggetto + (iOggetto / 3);
System.out.println("iOggetto2 dopo l'espressione: " + iOggetto2);
// La stessa espressione di prima viene valutata
// ma al risultato non viene applicato il boxing.
i = iOggetto + (iOggetto / 3);
System.out.println("i dopo l'espressione: " + i);
}
}
L'output è mostrato qui:
Valore originale di iOggetto: 100
Dopo ++iOggetto: 101
iOggetto2 dopo l'espressione: 134
i dopo l'espressione: 134
Nel programma, prestiamo particolare attenzione a questa riga:
++iOggetto;
Questo causa l'incremento del valore in iOggetto
. Funziona così:
- Viene applicato l'unboxing a
iOggetto
; - il suo valore viene incrementato;
- Al risultato viene riapplicato il boxing e il nuovo valore boxed viene memorizzato in
iOggetto
.
L'Auto-unboxing consente anche di mescolare diversi tipi di oggetti numerici in un'espressione. Una volta che i valori sono unboxed, vengono applicate le promozioni e conversioni di tipo standard. Ad esempio, il seguente programma è perfettamente valido:
class AutoBox4 {
public static void main(String[] args) {
Integer iOggetto = 100;
Double dOggetto = 98.6;
dOggetto = dOggetto + iOggetto;
System.out.println("dOggetto dopo l'espressione: " + dOggetto);
}
}
L'output è mostrato qui:
dOggetto dopo l'espressione: 198.6
Come si può vedere, sia l'oggetto Double
dOggetto
che l'oggetto Integer
iOggetto
hanno partecipato all'addizione, e al risultato è stato applicato il boxing e memorizzato in dOggetto
.
A causa dell'auto-unboxing, si possono usare oggetti numerici Integer
per controllare un'istruzione switch
. Ad esempio, consideriamo questo frammento di codice:
Integer iOggetto = 2;
switch(iOggetto) {
case 1:
System.out.println("uno");
break;
case 2:
System.out.println("due");
break;
default:
System.out.println("errore");
}
Quando l'espressione switch
viene valutata, viene applicato l'unboxing a iOggetto
e il suo valore int
viene restituito.
Come mostrano gli esempi precedenti, grazie all'autoboxing/unboxing, usare oggetti numerici in un'espressione è sia intuitivo che facile. Nelle vecchie versioni di Java, tale codice avrebbe coinvolto cast e chiamate a metodi come intValue()
.
Autoboxing e Auto-Unboxing di Valori Boolean
e Character
Come descritto in precedenza, Java fornisce anche wrapper per boolean
e char
. Questi sono Boolean
e Character
.
L'autoboxing/unboxing si applica anche a questi wrapper. Ad esempio, consideriamo il seguente programma:
// Autoboxing/unboxing di un Boolean e Character.
class AutoBox5 {
public static void main(String[] args) {
// Autobox/unbox di un boolean.
Boolean b = true;
// In questa istruzione, al valore di b
// viene applicato l'unboxing e il risultato
// viene usato per controllare l'istruzione if.
if(b)
System.out.println("b è true");
// Autobox/unbox di un char.
// Boxing di un char
Character ch = 'x';
// Unboxing di un char
char ch2 = ch;
System.out.println("ch2 è " + ch2);
}
}
L'output è mostrato qui:
b è true
ch2 è x
La cosa più importante da notare riguardo a questo programma è l'auto-unboxing di b
all'interno dell'espressione condizionale if
.
Come dovremmo ricordare, l'espressione condizionale che controlla un if
deve valutare al tipo boolean
. A causa dell'auto-unboxing, al valore boolean
contenuto all'interno di b
viene automaticamente applicato l'unboxing quando l'espressione condizionale viene valutata. Quindi, con l'autoboxing/unboxing, un oggetto Boolean
può essere usato per controllare un'istruzione if
.
A causa dell'auto-unboxing, un oggetto Boolean
può ora anche essere usato per controllare qualsiasi delle istruzioni di ciclo di Java. Quando un Boolean
viene usato come espressione condizionale di un while
, for
, o do/while
, viene automaticamente applicato l'unboxing nel suo equivalente boolean
. Ad esempio, questo è codice perfettamente valido:
Boolean b;
// ...
while(b) { // ...
Prevenzione degli Errori con Autoboxing
Oltre alla comodità che offre, l'autoboxing e l'auto-unboxing può anche aiutare a prevenire errori.
Ad esempio, consideriamo il seguente programma:
// Un errore prodotto da unboxing manuale.
class ErroreUnboxing {
public static void main(String[] args) {
// Autoboxing del valore 1000 in un Integer
Integer iOggetto = 1000;
// Unboxing manuale come byte
int i = iOggetto.byteValue();
// Questa riga non visualizza 1000!
System.out.println(i);
}
}
Questo programma visualizza non il valore atteso di 1000, ma –24!
La ragione è che il valore all'interno di iOggetto
è sottoposto a unboxing manuale chiamando byteValue()
, il che causa il troncamento del valore memorizzato in iOggetto
, che è 1000.
Questo risulta nel valore spazzatura di –24 che viene assegnato a i
. L'Auto-unboxing previene questo tipo di errore perché il valore in iOggetto
sarà sempre sottoposto ad auto-unboxing in un valore compatibile con int
.
In generale, poiché l'autoboxing crea sempre l'oggetto appropriato, e l'auto-unboxing produce sempre il valore appropriato, non c'è modo per il processo di produrre il tipo sbagliato di oggetto o valore.
Nelle rare istanze dove si desidera un tipo diverso da quello prodotto dal processo automatizzato, si può ancora effettuare boxing e unboxing manualmente dei valori. Naturalmente, i benefici di autoboxing/unboxing vengono persi. In generale, dovremmo impiegare autoboxing/unboxing. È il modo in cui viene scritto il codice Java moderno.
Quando non usare l'autoboxing
Grazie alle comodità dell'autoboxing e dell'auto-unboxing, alcuni potrebbero essere tentati di utilizzare oggetti come Integer
o Double
esclusivamente, abbandonando completamente i tipi primitivi.
Ad esempio, con l'autoboxing/unboxing è possibile scrivere codice come questo:
// Un cattivo uso dell'autoboxing/unboxing!
// Lati di un triangolo rettangolo
Double a, b, c;
// Cateto numero 1
a = 10.0;
// Cateto numero 2
b = 4.0;
// Calcolo dell'ipotenusa
c = Math.sqrt(a * a + b * b);
System.out.println("L'ipotenusa è " + c);
In questo esempio, oggetti di tipo Double
contengono valori che vengono utilizzati per calcolare l'ipotenusa di un triangolo rettangolo.
Sebbene questo codice sia tecnicamente corretto e funzioni effettivamente in modo appropriato, è un uso molto cattivo dell'autoboxing/unboxing. È molto meno efficiente del codice equivalente scritto utilizzando il tipo primitivo double
.
La ragione è che ogni autobox e auto-unbox aggiunge un overhead che non è presente se viene utilizzato il tipo primitivo.
In generale, si dovrebbe limitare l'uso dei wrapper dei tipi solo a quei casi in cui è richiesta una rappresentazione ad oggetto di un tipo primitivo.
L'autoboxing e l'auto-unboxing non sono stati aggiunti a Java come modi nascosti per eliminare i tipi primitivi.