giulio0 ha scritto:
int i;
boolean b;
i = (int) b; // non fattibile perché il boolean non può essere mai convertito
b = (boolean) i; //fattibile (credo)
NO, nessuno dei due è lecito. Il boolean NON può essere convertito a/da qualunque altro primitivo. Il boolean è un tipo "da solo", a sé stante.
-----------------
Partiamo da alcuni concetti: innanzitutto il cast può essere implicito solo se è un
widening (un "allargamento" o
up-cast). Se è un
narrowing ("restringimento" o
down-cast)
deve essere esplicito. C'è una sola situazione in cui un narrowing può essere implicito. Quando una
costante byte/short/char/int viene assegnata ad una variabile di tipo più piccolo e il valore della costante "ci sta" in quella variabile. Es.
byte b = 120;
120 è una costante
int. Ma ci sta come valore in un byte. E per via di questa regoletta speciale, questo narrowing implicito è lecito.
Il widening sui primitivi è abbastanza facile, perché detto in generale non fa "perdere" nulla. Ci sono 2 casi particolari però:
- da int a float
- da long a double
int e float sono entrambi a 32 bit. Ma il float ha meno bit per la parte significativa (perché c'è anche l'esponente). Quindi se passi da int a float mantieni sicuramente la "grandezza" del valore ma PUOI perdere in precisione.
Cosa similare per long->double entrambi a 64 bit.
Il narrowing sui primitivi invece è più grave. Detto in generale: puoi perdere la grandezza, la precisione e (attenzione!) pure il segno.
-----------------
Con i tipi reference il discorso è diverso. Bisogna avere ben chiaro cosa è lecito per il compilatore e cosa invece può succedere a runtime.
Il widening sui reference è facile (e può essere implicito). Basta assegnare ad un super-tipo, detto in generale (super-classe o interfaccia implementata).
String str = "ciao";
Object o = str; // 2
CharSequence cs = str; // 3
Object è una superclasse di String e CharSequence è una interfaccia implementata da String. Sono, in generale, "super tipi" rispetto a String, quindi nelle righe 2 e 3 c'è un widening implicito.
Il narrowing deve essere esplicito ed è un cast "controllato" a runtime. Per il compilatore è sufficiente che il cast teoricamente sia sensato. Non si può fare ad esempio un cast da un Number ad un String. Il compilatore "sa" già (lo deduce dai tipi) che NON sono in linea di ereditarietà.
Mentre invece ad esempio un Object può essere castato ad un String. Ma ... quale è l'oggetto realmente assegnato alla variabile Object fa sì che il cast a runtime abbia successo o fallisca.
Object o = "abc";
String s = (String) o;
Il cast è lecito (una variabile di tipo Object
può fare riferimento ad un String). E a runtime il cast ha successo, perché l'oggetto è realmente un String.
Object o = new Integer(123);
String s = (String) o;
Qui il cast è ancora sempre lecito per il compilatore ma FALLISCE a runtime con un bel ClassCastException. Perché l'oggetto non è realmente un String.
Ci sarebbero svariate altre cose da dire, specialmente sui cast tra classi e interfacce. Ma non posso fare un manualone in un post ...
Posso aggiungere una cosa: da Java 5, se non lo sai, esiste il auto-boxing/unboxing che permette di trattare i primitivi e le rispettive classi "wrapper" (Byte, Short, ecc...) in maniera praticamente interscambiabile.
Il Java Language Specification dice che è possibile fare un boxing di un primitivo opzionalmente seguito da un widening del reference. Tipo ad esempio:
int i = 123;
Object o = i;
La catena di cast è: int ---> Integer (per auto-boxing) e poi Integer ---> Object (per widening del reference).
Ed è anche possibile un unboxing opzionalmente seguito da un widening del primitivo, tipo in:
Integer i = new Integer(123);
long l = i;
Nota che non c'è un cast esplicito! Per via del auto-unboxing è come se quel
i "fosse" un int primitivo. Quindi Integer ---> int (per unboxing) e poi int ---> long (widening implicito)