Tipi di dato primitivi

Nel paradigma di programmazione a oggetti, i dati manipolabili coi programmi sono istanze di classi, detti oggetti (impareremo presto cosa sono).

Java non è un linguaggio a oggetti puro e mette invece a disposizione alcuni tipi di dato primitivi di uso comune per renderne più agevole la manipolazione.

I tipi di dato primitivi di Java sono elencati nella seguente tabella.

Tipi di dato primitivi di Java
byte interi tra -128 e 127 (8 bit)
short interi tra -32768 e 32767 (16 bit)
int interi tra -2147483648 e 2147483647 (32 bit)
long interi con 64 bit
float numeri in virgola mobile (32 bit)
double numeri in virgola mobile (64 bit)
char caratteri (standard UNICODE, 16 bit)
boolean due soli valori possibili: true e false

Quindi ci sono quattro tipi primitivi per numeri interi e due per numeri in virgola mobile.

Gli interi sono rappresentati in complemento a due.

L'intervallo di valori per ogni tipo di interi è fissato dal linguaggio (indipendente dalla piattaforma), a differenza ad esempio del linguaggio C dove lo stesso tipo può essere codificato con un diverso numero di bit a seconda della macchina dove si compila il programma.




Assegnamento tra variabili numeriche

In un comando di assegnamento, il valore dell'espressione a destra del simbolo = deve essere di tipo compatibile con il tipo della variabile.

Nel caso dei tipi di dato numerici, un tipo è compatibile con un altro se la conversione del valore non provoca una perdita di informazione: in questo caso la conversione è automatica.

int iBase = 74;
double dBase = iBase;
OK: Un int (32 bit in complemento a 2) può essere convertito in un double (64 bit in virgola mobile) senza perdita di informazione.
double dBase = 100;
int iBase = dBase;
NO: Un numero in virgola mobile non può essere convertito in un intero senza perdita di informazione (la parte decimale).

Il programmatore può comunque forzare una conversione tra tipi numerici (anche nel caso di perdita di informazione) utilizzando l'operatore di cast.

Il cast si scrive mettendo a sinistra dell'espressione da convertire il nome del tipo desiderato racchiuso tra parentesi:

<variabile-di-tipo1> = (<tipo1>) <espressione-di-tipo2>;
double pi = 3.14159;
int intPi = (int) pi;
System.out.println(intPi);
OK: Converte il numero decimale in un intero, perdendo la parte decimale: stampa 3.
double dBase = 12.234;
float fBase = (float) dBase;
OK: Converte il numero in virgola mobile da 64 a 32 bit, anche se perde informazione.




Costanti numeriche (1)

Le costanti numeriche (literals) si scrivono nella forma usuale:
  • Interi: 74, 563899289, 041 (33 scritto in ottale), 0xFF (255 scritto in esadecimale), ...
  • Numeri in virgola mobile: 3.14, 45, 35.78e2 (35.78 * 102 = 3578), ...
Attenzione ai tipi!
  • Le costanti numeriche intere di sole cifre sono al massimo di tipo int, non long
  • Le costanti numeriche in virgola mobile sono di tipo double, non float

Suggerimento: per evitare problemi, usare solo i tipi int e double.



Costanti numeriche (2)

Per cercare di capire quale tipo di dato il compilatore associa ad una costante numerica, si guardino con attenzione gli esempi mostrati nella seguente tabella (riportati nel programma CostantiNumeriche.java), ed il commento a fianco. Il modo migliore per comprenderli è eseguirli di persona e leggere l'eventuale messaggio di errore del compilatore.

short x = 256; OK
short x = 3565567; Errore: la costante intera è troppo grande per uno short
possible loss of precision
found   : int
required: short
long x = 44444442556; Errore: Alla costante viene assegnato il tipo int, ma il valore è troppo grande
integer number too large: 44444442556
long x = 44444442556l; OK: una "l" (L minuscola) alla fine indica che la costante è di tipo long
float y = 3.1; Errore: possible loss of precision
found   : double
required: float
float y = 3.1f; OK: "f" indica che la costante è float
float y = (float) 3.1; OK: l'operatore di cast converte la costante double in float prima dell'assegnamento



Operatori aritmetici (1)

In Java sono disponibili i seguenti operatori aritmetici, elencati in ordine decrescente di priorità.
Ad esempio 33 / 11 + 27 * 9 viene valutato come (33 / 11) + (27 * 9).
Inoltre tutti gli operatori associano a sinistra.
Ad esempio, 34 + 27 + 2 viene valutato come (34 + 27) + 2.

-
 negazione
++
incremento
--
decremento
*
moltiplicazione
/
divisione
%
modulo
+
addizione
-
sottrazione

Tutti gli operatori, se applicati a interi, restituiscono un valore int o long, mai byte o short.




Operatori aritmetici (2)

Gli operatori aritmetici usuali hanno il significato che ci si aspetta. Fa eccezione l'operatore di divisione, che può avere due diversi significati:
  • divisione intera, se entrambi gli argomenti sono interi: in questo caso il risultato è un intero (il quoziente della divisione tra interi);

  • divisione tra numeri in virgola mobile, se almeno uno degli argomenti è in virgola mobile.

System.out.println(7.0 / 4.0);
System.out.println(7.0 / 4);
System.out.println(7 / (double) 4);
System.out.println(7 / 4);
I primi tre stampano 1.75
L'ultimo stampa 1!!

L'operatore di modulo restituisce il resto della divisione intera.

Il modulo n % d è calcolato come segue:

n % d  =  n - ( (n/d) * d )

System.out.println(7 % 4);
Stampa 3.

Gli operatori di incremento e decremento possono essere applicati solo ad una variabile, e ne aumentano o diminuiscono il valore di un'unità. Questi operatori esistono in due versioni: prefissa e postfissa.

int n = 5, m = 5;
System.out.println(n++);
System.out.println(n);
System.out.println(++m);
System.out.println(m);
 
Stampa 5, valore vecchio di n.
Stampa 6.
Stampa 6, valore nuovo di m.
Stampa 6.

Il risultato di semplici operazioni su valori in virgola mobile può manifestare inattesi errori di approssimazione. Ad esempio,

System.out.println(2.1 - 2); Stampa 0.10000000000000009

Questo è dovuto al fatto che numeri decimali come 2.1 hanno, nella rappresentazione binaria, una espansione infinita (periodica), e quindi non possono essere rappresentati in modo esatto come double: la loro espansione sarà opportunamente troncata. Questo errore di rappresentazione si manifesta nella valutazione di certe espressioni.

Come in tutti i linguaggi di programmazione, anche in Java si possono verificare i fenomeni di overflow e underflow. As esempio, si assegni ad una variabile di tipo int il massimo intero rappresentabile, 2147483647, le si aggiunga 1 e si stampi il risultato: quale sarà?

A differenza di altri linguaggi, in Java le situazioni di overflow e underflow non dipendono dal computer.




Ancora sull'assegnamento

In Java esistono altri operatori di assegnamento (eredità di C e C++), che sono semplicemente delle abbreviazioni sintattiche.

int x = 5;
double y = 3.14;
x += 7;
y *= 2.5;
x -= 7;
y /= 4;
int variabileConNomeLungo = 0;
variabileConNomeLungo += 10;
 
 
x = x + 7;
y = y * 2.5;
x = x - 7;
y = y / 4;
 
 

Un comando di assegnamento è anche un'espressione, che restituisce il valore assegnato.

System.out.println(x = 74); Assegna 74 a x e stampa 74
x = y *= 3; Associa a destra: equivale a
x = (y = y * 3).

Abbiamo visto diversi modi per incrementare una variabile:

x = x + 1
x += 1
x++
++x

Sono tutti equivalenti?

Raccomandazioni per una buona programmazione :

  • Usare le parentesi nelle espressioni per dimostrare che le state scrivendo nel modo giusto
  • Evitare assolutamente l'uso di assegnamenti all'interno di altre espressioni
  • Limitare l'uso di assegnamenti multipli alle sole inizializzazioni