1. Introduzione
Nel linguaggio Java esistono due diverse famiglie di dati. La prima — sono i tipi primitivi: int, char, double, boolean e altri. Sono semplici e veloci: il valore è memorizzato direttamente nella cella di memoria. La seconda famiglia — sono i tipi di riferimento, cioè oggetti: la variabile contiene un riferimento al luogo in cui l'oggetto è situato in memoria.
A volte è necessario usare un primitivo come un oggetto. Ma i primitivi, di per sé, non sono oggetti. In questi casi aiutano le speciali classi wrapper.
I tipi wrapper sono classi che contengono al loro interno il valore di un primitivo e permettono di trattarlo come un oggetto. Ad esempio, per int — la classe Integer, per double — Double, per char — Character, per boolean — Boolean. Tali classi forniscono metodi: Integer.parseInt(), Double.isInfinite(), Character.isLetter(), Boolean.parseBoolean() e altri.
Esempio di ottenere un oggetto Integer da un primitivo int:
// Primitivo
int a = 10;
// Oggetto wrapper
Integer b = Integer.valueOf(10);
Nella variabile a è memorizzato il numero 10 stesso. La variabile b invece contiene un riferimento a un oggetto che al suo interno contiene 10.
2. Perché servono i wrapper
Un primitivo è sempre un valore concreto, non può essere uguale a null. Un oggetto può non esistere e contenere un valore vuoto — è comodo quando bisogna indicare «sconosciuto» o «assente».
Un primitivo non ha metodi. Non si può invocare su un numero qualcosa come x.toString(). Un wrapper offre tali possibilità: Integer.parseInt("123") trasforma una stringa in un numero, e un numero può essere convertito in stringa con toString().
In sintesi: i wrapper servono dove è necessario un oggetto — la possibilità di memorizzare null, chiamare metodi, passare valori a un'API che accetta solo oggetti (ad esempio, le collezioni come List<Integer>).
Elenco completo delle classi wrapper
| Tipo primitivo | Classe wrapper |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3. Boxing e unboxing
Quando create manualmente un oggetto wrapper, prendete un primitivo e lo «incapsulate» all'interno dell'oggetto:
int primitive = 42;
Integer wrapper = Integer.valueOf(primitive); // boxing
Per recuperare il valore primitivo — fare l'unboxing — si usa, ad esempio, intValue():
Integer wrapper = Integer.valueOf(42);
int primitive = wrapper.intValue(); // unboxing
Autoboxing (Autoboxing)
Il compilatore aggiungerà automaticamente la chiamata a valueOf() se assegnate un primitivo a una variabile wrapper:
int a = 10;
Integer b = a; // autoboxing (Integer.valueOf(a))
Unboxing (Unboxing)
Situazione inversa — quando un oggetto wrapper viene usato dove serve un primitivo:
Integer c = 20;
int d = c; // unboxing (c.intValue())
int a = 10; // primitivo
Integer b = a; // autoboxing
Integer c = Integer.valueOf(20);
int d = c; // unboxing
4. Insidie
Confronto dei wrapper. L'operatore == per gli oggetti confronta i riferimenti, non i valori:
Integer m = 200;
Integer n = 200;
System.out.println(m == n); // false, perché sono oggetti diversi
Java mette in cache i wrapper dei numeri nell'intervallo da -128 a 127, quindi a volte == restituirà true e altre — false. Per confrontare i valori usate sempre equals():
Integer x = 100;
Integer y = 100;
System.out.println(x == y); // true, rientra nella cache
System.out.println(x.equals(y)); // true, confronto per valore
NullPointerException durante l'unboxing. Se un wrapper è uguale a null e provate a fare l'unboxing — otterrete un'eccezione:
Integer value = null;
int primitive = value; // Errore! NullPointerException
Prestazioni e memoria. I wrapper sono più lenti dei primitivi e occupano più memoria. Nelle parti calde del codice e quando si lavora con grandi array di numeri, scegliete i primitivi.
5. Esempi
Esempi con Integer e Double
Conversione di una stringa in numero:
String text = "123";
int number = Integer.parseInt(text);
System.out.println(number); // 123
Verifica di valori speciali dei numeri in virgola mobile:
double d = 1.0 / 0;
System.out.println(Double.isInfinite(d)); // true
double nan = 0.0 / 0.0;
System.out.println(Double.isNaN(nan)); // true
Esempi con Character
char memorizza un singolo carattere; il wrapper Character offre comode verifiche:
char ch = 'A';
Character wrapper = ch; // autoboxing
System.out.println(Character.isLetter(ch)); // true
System.out.println(Character.isDigit(ch)); // false
System.out.println(Character.toLowerCase(ch)); // 'a'
Esempi con Boolean
Il primitivo boolean è solo true/false. Il wrapper Boolean consente di memorizzare anche null:
Boolean flag = null; // consentito
flag = Boolean.TRUE; // costante speciale
System.out.println(flag); // true
Conversione di stringhe in valori booleani:
String s1 = "true";
String s2 = "false";
boolean b1 = Boolean.parseBoolean(s1);
boolean b2 = Boolean.parseBoolean(s2);
System.out.println(b1); // true
System.out.println(b2); // false
GO TO FULL VERSION