Conversione dei Tipi di Dato in Java

Concetti Chiave
  • In Java, è possibile convertire i tipi di dato tra loro, sia automaticamente che tramite casting esplicito.
  • Le conversioni automatiche avvengono quando i tipi sono compatibili e il tipo di destinazione è più grande.
  • Il casting consente di convertire esplicitamente tra tipi incompatibili, ma può comportare la perdita di dati.
  • Le promozioni automatiche dei tipi avvengono nelle espressioni per garantire la precisione dei calcoli.

Conversione di Tipo e Casting

Se si ha esperienza di programmazione, risulta piuttosto comune assegnare un valore di un tipo a una variabile di un altro tipo.

Se i due tipi sono compatibili, Java effettua automaticamente la conversione. Ad esempio, è sempre possibile assegnare un valore int a una variabile long. Tuttavia non tutti i tipi sono compatibili e, di conseguenza, non tutte le conversioni di tipo sono consentite implicitamente.

Per esempio, non esiste alcuna conversione automatica da double a byte. Fortunatamente è comunque possibile ottenere una conversione tra tipi incompatibili.

Per farlo è necessario usare un cast, che esegue una conversione esplicita tra tipi incompatibili. Di seguito vengono esaminate sia le conversioni automatiche di tipo sia il casting.

Conversioni Automatiche di Java

Quando un tipo di dato viene assegnato a una variabile di un altro tipo, viene eseguita una conversione automatica di tipo se sono soddisfatte le due condizioni seguenti:

  • I due tipi sono compatibili.
  • Il tipo di destinazione è più grande del tipo di origine.

Quando queste due condizioni sono soddisfatte, avviene una conversione di ampliamento. Ad esempio, il tipo int è sempre abbastanza grande da contenere tutti i valori validi di byte, quindi non è richiesto alcun cast esplicito.

Per le conversioni di ampliamento, i tipi numerici, inclusi i tipi interi e floating-point, sono compatibili tra loro. Tuttavia non esistono conversioni automatiche dai tipi numerici a char o boolean. Inoltre char e boolean non sono compatibili tra loro.

Come accennato in precedenza, Java esegue anche una conversione automatica di tipo quando si memorizza una costante intera letterale in variabili di tipo byte, short, long o char.

Casting di Tipi Incompatibili

Sebbene le conversioni automatiche di tipo siano utili, non coprono tutte le esigenze. Ad esempio, se occorre assegnare un valore int a una variabile byte, questa conversione non verrà eseguita automaticamente, poiché un byte è più piccolo di un int.

Questo tipo di conversione è talvolta chiamato conversione di restringimento, poiché si riduce esplicitamente il valore affinché rientri nel tipo di destinazione.

Per creare una conversione tra due tipi incompatibili è necessario utilizzare un cast. Un cast è semplicemente una conversione di tipo esplicita. Ha la forma generale:

(tipo-destinazione) valore

Qui tipo-destinazione specifica il tipo desiderato a cui convertire il valore indicato.

Ad esempio, il frammento seguente effettua il cast di un int a byte. Se il valore dell'intero è maggiore dell'intervallo di un byte, esso verrà ridotto in modulo (il resto di una divisione intera per) l'intervallo del byte.

int a;
byte b;
// ...
b = (byte) a;

Un tipo diverso di conversione si verifica quando un valore floating-point viene assegnato a un tipo intero: troncamento.

Gli interi non hanno componenti frazionarie. Pertanto, quando un valore floating-point viene assegnato a un tipo intero, la parte frazionaria viene persa. Ad esempio, se il valore 1.23 viene assegnato a un intero, il valore risultante sarà semplicemente 1; lo 0.23 sarà stato troncato. Naturalmente, se la parte intera è troppo grande per rientrare nel tipo intero di destinazione, quel valore verrà ridotto in modulo rispetto all'intervallo del tipo di destinazione.

Il programma seguente dimostra alcune conversioni di tipo che richiedono cast:

// Dimostrare i cast.
class Conversione {
    public static void main(String[] args) {
        byte b;
        int i = 257;
        double d = 323.142;

        System.out.println("\nConversione da int a byte.");
        b = (byte) i;
        System.out.println("i e b " + i + " " + b);

        System.out.println("\nConversione da double a int.");
        i = (int) d;
        System.out.println("d e i " + d + " " + i);

        System.out.println("\nConversione da double a byte.");
        b = (byte) d;
        System.out.println("d e b " + d + " " + b);
    }
}

Questo programma genera il seguente output:

Conversione da int a byte.
i e b 257 1

Conversione da double a int.
d e i 323.142 323

Conversione da double a byte.
d e b 323.142 67

Esaminando ciascuna conversione: quando il valore 257 viene convertito in una variabile byte, il risultato è il resto della divisione di 257 per 256 (l'intervallo di un byte), che in questo caso è 1. Quando d viene convertito in int, la sua componente frazionaria viene persa. Quando d viene convertito in byte, la componente frazionaria viene persa e il valore viene ridotto in modulo 256, che in questo caso è 67.

Promozione Automatica dei Tipi nelle Espressioni

Oltre alle assegnazioni, esiste un altro contesto in cui possono verificarsi determinate conversioni di tipo: nelle espressioni.

Per comprendere il motivo, si consideri quanto segue. In un'espressione, la precisione richiesta da un valore intermedio può talvolta superare l'intervallo di uno qualsiasi degli operandi. Per esempio, si esamini l'espressione seguente:

byte a = 40;
byte b = 50;
byte c = 100;
int d = a * b / c;

Il risultato del termine intermedio a * b supera facilmente l'intervallo di ciascuno dei suoi operandi di tipo byte. Per gestire questo problema, Java promuove automaticamente ogni operando byte, short o char a int durante la valutazione di un'espressione.

Ciò significa che la sotto-espressione a * b viene eseguita usando interi—non byte. Quindi, 2 000, risultato dell'espressione intermedia 50 * 40, è valido anche se a e b sono entrambi dichiarati di tipo byte.

Per quanto utili siano queste promozioni automatiche, esse possono generare errori di compilazione fuorvianti. Ad esempio, il codice apparentemente corretto seguente causa un problema:

byte b = 50;
b = b * 2; // Errore! Impossibile assegnare un int a un byte!

Il codice tenta di memorizzare 50 * 2, un valore perfettamente valido per un byte, in una variabile byte. Tuttavia, poiché gli operandi sono stati automaticamente promossi a int durante la valutazione dell'espressione, anche il risultato è stato promosso a int.

Di conseguenza, il risultato dell'espressione è ora di tipo int, che non può essere assegnato a un byte senza l'uso di un cast. Ciò vale anche se, come in questo caso specifico, il valore assegnato rientrerebbe comunque nell'intervallo del tipo di destinazione.

Quando le conseguenze di un eventuale overflow sono note, è opportuno utilizzare un cast esplicito, ad esempio:

byte b = 50;
b = (byte)(b * 2);

che produce il valore corretto di 100.

Le Regole di Promozione dei Tipi

Java definisce diverse regole di promozione dei tipi che si applicano alle espressioni:

  1. Tutti i valori byte, short e char vengono promossi a int, come descritto in precedenza.
  2. Se uno degli operandi è un long, l'intera espressione viene promossa a long.
  3. Se uno degli operandi è un float, l'intera espressione viene promossa a float.
  4. Se uno qualsiasi degli operandi è double, il risultato è double.

Il programma seguente dimostra come ciascun valore nell'espressione venga promosso per corrispondere al secondo argomento di ogni operatore binario:

class Promozione {
    public static void main(String[] args) {
        byte b = 42;
        char c = 'a';
        short s = 1024;
        int i = 50000;
        float f = 5.67f;
        double d = .1234;
        double risultato = (f * b) + (i / c) - (d * s);
        System.out.println((f * b) + " + " + (i / c) + " - " + (d * s));
        System.out.println("risultato = " + risultato);
    }
}

Esaminare ora da vicino le promozioni di tipo che si producono in questa riga del programma:

double risultato = (f * b) + (i / c) - (d * s);
  • Nella prima sotto-espressione, f * b, b viene promosso a float e il risultato della sotto-espressione è float.
  • Nella sotto-espressione i / c, c viene promosso a int e il risultato è di tipo int.
  • In d * s, il valore di s viene promosso a double, e la sotto-espressione è di tipo double.

Infine, questi tre valori intermedi, float, int e double, vengono considerati insieme. La somma di un float e di un int produce un float. Successivamente, il float risultante meno l'ultimo double viene promosso a double, che è il tipo del risultato finale dell'espressione.