Tricky Java Things parte 1: Autoboxing vs Widening

In Java sono presenti alcune agevolazioni sintattiche il cui scopo è evitare di scrivere parti di codice ripetitive e poco utili.

La prima di queste agevolazioni è utilizzata dal 100% degli sviluppatori Java. Si tratta dell'autoboxing, presente dalla versione 5 di Java, che permette di usare in maniera intercambiabile tipi primitivi (int, long,...) e le rispettive wrapper class (Integer, Long,...). La seconda,  utilizzata spesso in maniera implicita, è detta widening, ed è presente fin dalla prima versione di Java.

L'uso di combinato di questi due meccanismi può talvolta avere effetti curiosi e un po' subdoli. 

Cosa accade - ad esempio - se scriviamo il metodo:

public static void doSomething(Long l) {...}

e poi lo invochiamo così:

public static void main(String[] args)
   {
    byte b = 42;
    doSomething(b);
   }

Accade che il compilatore segnala un errore, per l'impossibilità di fare prima il widening poi l'autoboxing della variabile byte.

Cosa accade invece se il metodo doSomething viene scritto così

public static void doSomething(Object obj){...} e lo invochiamo usando lo stesso main visto in precedenza?

La risposta è che in questo caso il codice compila regolarmente.

Perché accade questo? Vediamo i passi uno ad uno: 

1 - Il byte viene convertito in Byte (autoboxing)
2 - Il Byte viene utilizzato per widening come un Object dal metodo doSomething.

Volendo ottenere nuovamente il byte di origine si può fare un cast dentro il corpo di doSomething:  Byte b = (Byte)obj, perché il widending ha comunque un riferimento ad un oggetto che di fatto è un Byte.

Sembra naturale, no?

Allora perché nel primo caso il codice non compila?

La risposta è nella linea di ereditarietà delle wrapper class. Prima viene fatto l'autoboxing da byte a Byte e poi si cerca di fare il widening verso Long, ma il widening fra oggetti funziona solo se esiste un legame di ereditarietà tra gli oggetti coinvolti: Long non ha legami con Byte, quindi non lo si può passare al metodo; al contrario Byte deriva da Object, quindi il codice compila regolarmente.

Niente di complesso, insomma, ma semplicemente qualcosa a cui fare attenzione quando si scrive il codice. 
Riassumendo:

NON si può fare il widening da una wrapper class all'altra come di farebbe coi tipi primitivi

NON si può fare in sequenza il widening e poi il boxing 
(un int non potrà mai divenire un Long)


Si può fare il boxing e poi il widening in sequenza (un int può diventare Object in quanto "boxato" a Integer).



Share on Google Plus

About Andrea Castello

Time traveller, dev, Rory's dad, old surrealist guy from Sardegna, Italia
    Blogger Comment
    Facebook Comment

0 commenti :

Posta un commento