Sei sulla pagina 1di 176

Programmazione in Java

Il linguaggio Java
• Progettato da Sun Microsystems (ora Oracle)
nel 1995/96
– Ora offerto con licenza GNU GPL

• Useremo la Java Standard Edition (SE)


– Versione 8 o superiore
– Ultima versione: 15

• Esistono anche
– Java Enterprise Edition
• Costruita sopra Java SE, offre servlets e JSP, EJB…
• Per sviluppare applicazioni "server side"
– Java Micro Edition
• Per piccoli dispositivi
• Ormai obsoleta (Android, …)
Caratteristiche
• Object-oriented (OO)
– Da Java 8 offre anche astrazioni funzionali

• Indipendente dalla piattaforma


– Bytecode e Java Virtual Machine

• Supporto alla distribuzione


– RMI e altri strumenti di distribuzione

• Sicuro
– Esecuzione in una "sandbox" che garantisce che non si possa
danneggiare l'host
Java 4
CLASSI COME ASTRAZIONI
La struttura di un programma Java
• Un programma Java è organizzato come un insieme di classi
– Ogni classe corrisponde a una dichiarazione di tipo

• Una classe contiene dichiarazioni di variabili (attributi) e di


funzioni (metodi)
– Il valore degli attributi caratterizzano le singole istanze (oggetti)
di una classe
– I metodi servono per manipolare le istanze
• Hanno sintassi analoga alle funzioni C

• Il programma principale è rappresentato da un metodo


speciale di una classe, detto main

6
Il primo programma Java

public class HelloWorld {


public static void main(String[] args){
System.out.println("Hello world!");
}
}

7
Esempio di classe in Java
public class Data {
private int giorno;
private int mese;
private int anno;

// restituisce il giorno
public int leggiGiorno(){…}

// restituisce il mese
public int leggiMese(){…}

// restituisce l’anno
public int leggiAnno(){…}
}
8
Il costrutto class
• Una classe può essere vista come un tipo definito
dall’utente che specifica le operazioni utilizzabili sul tipo
stesso

• Il tipo può essere usato per dichiarare altre variabili


– int a, b;
• Dichiara due variabili a e b sulle quali è possibile fare tutte le
operazioni predefinite per il tipo int
– Data d;
• Dichiara una variabile d sulla quale è possibile fare tutte le operazioni
definite nella classe Data

• Definisce un tipo di dato astratto

9
Oggetti
• Tutti gli oggetti della stessa classe hanno la stessa
struttura
– Il numero e tipo dei loro attributi è lo stesso
– Ad esempio, la classe Data definisce tutte le date possibili,
tramite gli attributi giorno, mese, anno

• Ogni oggetto, in ogni istante dell’esecuzione del


programma, è caratterizzato da uno stato, che è dato
dal valore degli attributi dell’oggetto
– Lo stato dell’oggetto “oggi” di tipo Data è definito dal
valore degli attributi giorno, mese, anno corrispondenti
alla data di oggi

10
Accesso ad attributi e metodi
• Tramite la "notazione punto"

• Esempio: Data d;
int x;
//codice che inizializza d
x = d.leggiGiorno();

– Invochiamo il metodo leggiGiorno() sull’oggetto d:


restituisce valore del giorno della data d
– È come se leggiGiorno() avesse come argomento implicito
d: in C scriveremmo: leggiGiorno(d);
– Si dice anche che all’oggetto d inviamo il messaggio
leggiGiorno

11
Cambiamenti di stato
• Lo stato degli oggetti può cambiare nel tempo, attraverso
l’invocazione di metodi opportuni

data1 data1
Data Data
giorno = 30 data1.giornoDopo() giorno = 31
mese = 12 mese = 12
anno = 2010 anno = 2010

• Esistono alcuni casi in cui gli oggetti sono immutabili, cioè


non possono essere modificati
– Ad esempio la classe predefinita String

12
metodo giornoDopo()
• Per modificare lo stato, il metodo deve potere accedere ai
campi dell’oggetto su cui è stato chiamato

• Nella definizione di un metodo ci si può riferire direttamente


(senza notazione punto) ad attributi e metodi dell’oggetto sul
quale si sta lavorando
public void giornoDopo() {
giorno++;
if (giorno > 31) {
giorno = 1;
mese++;
}
if (mese > 12) {
mese = 1;
anno++;
}
}
13
Private e public
• Attraverso i metodi "public" di una classe è
possibile vedere qual è lo stato di un oggetto ...
Data d;
int x;

x = d.leggiGiorno();

• … ma non posso accedere direttamente ai dati


"private"
if (d.mese == 12) //Errore di compilazione!

14
Tipi primitivi e variabili
• Tipi numerici: • Dichiarazione
– byte: 8 bit – byte unByte;
– short: 16 bit – int a, b=3, c;
– int: 32 bit – char c=‘h’, car;
– long: 64 bit
– boolean trovato=false;
– float: 32 bit
– double: 64 bit

• Altri tipi:
– boolean: true, false
– char: 16 bit, Unicode

15
Valori di default
Data Type Default Value (for fields)

byte 0

short 0

int 0

long 0L

float 0.0f

double 0.0d

char \u0000'

String (or any object) null

boolean false

16
Tipi riferimento
• Le variabili di tipo primitivo (numerico, char, bool)
contengono direttamente il valore

• Tutte le altre contengono riferimenti a valori


– Tipi definiti dall’utente (classi, interfacce)
– Array ed enumerazioni

• Ogni variabile dichiarata da una classe contiene un


riferimento a un oggetto
– Un indirizzo di memoria
– Il valore effettivo dell’indirizzo non è accessibile al
programmatore e non interessa

17
Tipi riferimento
• Un oggetto è memorizzato in un’opportuna
area di memoria
– d, di tipo Data, contiene l’indirizzo della prima
cella dell’oggetto

d xxx 3 giorno
11 mese
2010 anno

18
Variabili e tipi riferimento
• Tipi riferimento e primitivi possono essere utilizzati in
maniera (quasi) intercambiabile
– Dichiarazioni di variabili e parametri, tipo del valore
restituito da un metodo

• Le variabili
– Consentono di accedere agli oggetti
– Sono allocate nello stack quando esegue il metodo dove
sono dichiarate
– Gli oggetti referenziati dalle variabili sono allocati sullo
heap
– Le variabili sono deallocate quando il metodo termina

19
Dichiarazione e inizializzazione
• La dichiarazione di una variabile di tipo riferimento non
alloca spazio per un oggetto, ma solo per il riferimento ad
un oggetto

• A una variabile di tipo riferimento è assegnato inizialmente


il riferimento null, per indicare che la variabile non è ancora
associata ad alcun oggetto

• Data d;
d vale null, non esiste ancora un oggetto di tipo Data

20
Dichiarazione e inizializzazione
• Un parametro o una variabile locale non
possono essere usati senza essere inizializzati
– Il compilatore rileva staticamente mancanza di
inizializzazione

• In entrambi i casi occorre costruire un oggetto


e inizializzarlo esplicitamente

21
New
• La costruzione di un oggetto si realizza dinamicamente tramite
l’operatore new

• Esempio
– Data d = new Data();

• Effetto di new
– Costruisce un nuovo oggetto di tipo Data
– Restituisce il riferimento all’oggetto appena creato
• Il riferimento viene conservato nella variabile d per potere usare l’oggetto
– Data() è un metodo particolare chiamato costruttore
• Ha lo stesso nome della classe, si riconosce come metodo dalle parentesi ()

22
Dichiarazione e creazione
Data data;
• Il riferimento al primo
data = new Data();
oggetto Data è perso
data = new Data();

• Non c’è più modo di


data accedere all’oggetto

Data Data • L’oggetto verrà distrutto


dal garbage collector
Creato con
la prima new

23
Costruttori di default
• Se non si definisce nessun costruttore, il compilatore
fornisce il costruttore di default (senza parametri), che
svolge le seguenti funzioni
– Alloca lo spazio per gli attributi di tipo primitivo
– Alloca lo spazio per i riferimenti agli attributi di tipo
definito dall’utente
– Inizializza a null tutti i riferimenti, a 0 tutte le variabili
numeriche, a false tutti i boolean

• Se lo sviluppatore fornisce dei costruttori


personalizzati, il compilatore non fornisce quello di
default

24
I costruttori: esempio
public class C {
private int i1;
private int i2;
}

public class Esempio {


public static void main(String args[]) {
C x;
x = new C(); Corretto, non c’è costruttore
x = new C(5,7); personalizzato, il compilatore
} fornisce quello di default
}

Errore a compile-time, non c’è un


costruttore che prende due parametri

25
Costruttori personalizzati
public class C {
private int i1;
private int i2;

public class Esempio {


public C(int a1, int a2) {
public static void main(String args[]) { i1 = a1; i2 = a2;
C x, y, z; }

x = new C();
public C(int a) {
y = new C(1);
z = new C(1,4); i1 = a; i2 = a;
} }
}

Corretto, usa il Corretto, usa il Errore a compile-time, avendo


secondo costruttore primo costruttore fornito costruttori personalizzati,
il compilatore non fornisce
quello di default

26
Esempio di costruttore
public Data(int g, int m, int a){
giorno = g;
mese = m;
d xxx 20 giorno
anno = a;
} 3 mese
2007 anno
Data d = new Data(20,3,2007);

• Crea un oggetto d di tipo Data e lo inizializza al 20/03/2007

• Un’implementazione più sofisticata potrebbe controllare che


la data proposta sia legale, rifiutando il 31/2/2007

27
Inizializzazione e accesso
public class Persona {
private String nome;
Persona
private Persona padre;
private int eta; String nome
public Persona(String n) {
Persona padre
nome = n; padre = null; eta = 0;
} setEta int eta
public void setPadre(Persona p) { padre =p; }
public void setEta(int e) { eta = e; } setPadre
}
paolo pietro
public class Esempio {
public static void main(String[] args) {
Persona paolo, pietro; i
int i = 20; 30
20
paolo = new Persona("Paolo");
paolo.setEta(i);
Persona Persona
i = 30; // quanto vale paolo.eta?
pietro = new Persona("Pietro"); nome = “Paolo” nome =“Pietro”
pietro.setEta(i);
padre = padre =
paolo.setPadre(pietro);
// quanto vale paolo.padre.eta? setEta eta = 20
0 setEta eta = 31
30
0
pietro.setEta(i+1);
setPadre setPadre
// quanto vale paolo.padre.eta ?
}
}
28
Creazione e distruzione
• Se l’implementazione deve essere private, l’unico modo per inizializzare
un oggetto è specificare uno o più costruttori
– La creazione di un oggetto comporta sempre l’invocazione di un costruttore

• Il costruttore svolge due operazioni fondamentali, obbligandoci a definirle


insieme
– L’allocazione della memoria necessaria a contenere l’oggetto
– L’inizializzazione dello spazio allocato, assegnando opportuni valori

• A differenza del C, in Java non è necessario deallocare esplicitamente gli


oggetti
– Il garbage collector si occupa di questo: è una routine di sistema che provvede
automaticamente a liberare memoria quando serve
– Non necessariamente disponibile in altri linguaggi che supportano OO, come
C++

29
Condivisione (sharing o aliasing)
• Un oggetto è condiviso tra due
variabili se entrambe Data d1, d2;
accedono a esso d1 = new Data(20,3,2007);

d2 = d1;
• L'assegnamento di variabili di d2.giornoDopo();

tipo riferimento genera // Cosa stampa?


condivisione System.out.println(d1.getGiorno());

• Se oggetti condivisi sono


modificabili, le modifiche d1 xxx 20
21 giorno
apportate attraverso una 3 mese
variabile sono visibili anche d2 xxx 2007 anno
attraverso l'altra

30
Allocazione stack vs. heap
• Quando un metodo termina, tutte le variabili del corrispondente record di
attivazione sono distrutte

• … però gli oggetti creati sullo heap non sono necessariamente distrutti
public Data foo() {
Data d = new Data(1,1,2005);
d xxx 1
return d;
} 1
2010
public static void main(String[] args) {
Data x = foo();
}
x xxx
• Il riferimento d esiste solo sullo stack durante l’esecuzione di foo(), ma viene
deallocata quando foo() termina
– L’oggetto costruito continua a vivere sullo heap !!!
– Il garbage collector non pulisce la memoria perché x continua ad avere un
riferimento all’oggetto sullo heap

31
Tipi array
• Dato un tipo T (predefinito o definito dall’utente)
un array di T è definito come
– T[]

• Similmente sono dichiarati gli array


multidimensionali
– T[][] T[][][] …

• Esempi
– int[] float[] Persona[]

32
Dichiarazione e inizializzazione
• Dichiarazione
– int[] ai1, ai2;
– float[] af1;
– double ad[];
– Persona[][] ap;

• Inizializzazione
– int[] ai={1,2,3};
– double[][] ad={{1.2, 2.5}, {1.0, 1.5}};

33
Il caso degli array
• In mancanza di inizializzazione, la dichiarazione di un
array non alloca spazio per gli elementi dell’array

• L’allocazione si realizza dinamicamente tramite


l’operatore:
– new <tipo> [<dimensione>]

– int[] i = new int[10];


– i={10,11,12};
– float[][] f = new float[10][10];
– Persona[] p = new Persona[30];

• Se gli elementi non sono di un tipo primitivo,


l’operatore “new” alloca solo lo spazio per i riferimenti
34
Array di oggetti: definizione
A Person[ ] person;
person = new Person[20]; È definita solo la variabile
person[0] = new Person( ); person, l’array vero e
proprio non esiste

person

35
Array di oggetti: definizione
Person[ ] person;
Ora l’array è stato creato ma i
B person = new Person[20]; diversi oggetti di tipo Person
non esistono ancora ...
person[0] = new Person( );

person

0 1 2 3 4 16 17 18 19

36
Array di oggetti: definizione
Person[ ] person;
Un oggetto di tipo persona è stato creato e
person = new Person[20];
un riferimento a tale oggetto è stato
C person[0] = new Person( ); inserito in posizione 0

person

0 1 2 3 4 16 17 18 19

Person

37
Array di oggetti vs. array di tipi base
L’istruzione: float f[] = new float[10];
crea un oggetto di tipo array di float e alloca spazio per 10 float

f
0 1 2 3 4 5 6 7 8 9
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0

L'istruzione: Person p[] = new Person[10];


crea un oggetto di tipo array di Person e alloca spazio per 10 riferimenti
a oggetti di tipo Person
f
0 1 2 3 4 5 6 7 8 9
Non viene allocato
spazio per gli oggetti
veri e propri

38
Iterare negli array
int sum(int[] a) {
int result = 0;
for (int n : a) // Si legge “for each n in a”
result += n;
return result;
}

• La variabile n indica il generico elemento dell'array, non l'indice!

• Un esempio più chiaro: array di Person


int sumAge(Person[] a) {
int result = 0;
for (Person p: a)
result += p.getAge();
return result;
}

39
Chiamata di metodi
• Vengono valutati i parametri attuali

• Viene creato il record di attivazione sullo stack


– Spazio per i parametri formali
– Spazio per le variabili locali

• Esecuzione del corpo

• Regola per il passaggio parametri


– I parametri il cui tipo è uno dei tipi semplici sono passati per copia
– I parametri il cui tipo è un tipo riferimento sono passati per
riferimento

40
Passaggio parametri e reference
public class Data {
private int giorno;
private int mese;
private int anno; d2 xxx 1
3
1
2
public void copiaIn(Data d){
d.giorno = giorno; 1990
1984
d.mese = mese;
d.anno = anno;
d1 yyy 3
}
2

public static void main(String[] args){ 1984


Data d1 = new Data(3,2,1984);
Data d2 = new Data(1,1,1990);
d1.copiaIn(d2);
}
}
41
Visibilità dei nomi
• Le variabili locali ad un metodo (o i parametri
formali) possono mascherare gli attributi della
classe

• Soluzione:
– La pseudo-variabile this contiene un riferimento
all’oggetto corrente e può essere utilizzata per
aggirare eventuali mascheramenti

42
Aggirare mascheramenti
• this contiene un riferimento all’oggetto corrente e può
essere utilizzata per aggirare eventuali mascheramenti
– Quindi a.marca diventa "Ford", a.modello diventa "T4"

public class Automobile {


private String colore, marca, modello;

public void trasforma(String marca, String modello){


this.marca = marca;
this.modello = modello;
}

public static void main(String[] args){
Automobile a = new Automobile();
a.trasforma("Ford", "T4");
}
}

43
Un altro esempio
public class Data {
private int giorno;
private int mese;
private int anno;

public void giornoDopo() {


this.giorno++;
if (this.giorno > 31) {
this.giorno = 1;
this.mese++;
}

}

public static void main(String[] args) {


Data d1 = new Data();
Data d2 = new Data();
d1.giornoDopo(); // In giornoDopo this e’ lo stesso riferimento di d1
d2.giornoDopo(); // In giornoDopo this e’ lo stesso riferimento di d2
}
}

44
Restituire un riferimento
• La pseudo-variabile this può essere utilizzata per restituire un
riferimento all'oggetto corrente
public class InsiemeDiInteri {

public InsiemeDiInteri inserisci(int i) {
//modifica this inserendovi l’elemento i
return this; //restituisce l’insieme modificato
}

}

InsiemeDiInteri x,y,z;
//qui x e y sono inizializzati opportunamente
z = (x.inserisci(2)).unione(y)

//utile che inserisci restituisca un insieme


45
Metodi e attributi di classe
• Sintassi: static <definizione dell’attributo o metodo>

• Un attributo di classe è condiviso da tutti gli oggetti della classe

• Si può accedere a un attributo di classe (se public) senza bisogno di


creare un oggetto tramite la notazione:
– <nome classe>.<nome attributo>

• Un metodo di classe può essere invocato senza bisogno di creare un


oggetto tramite la notazione:
– <nome classe>.<nome metodo>(<par. attuali>)
– Il nome di classe può essere omesso se non ci sono problemi di
ambiguità

46
Metodi e attributi di classe: vincoli
• Un metodo static può accedere ai soli attributi
e metodi static

• Un metodo convenzionale può accedere


liberamente a metodi e attributi static

47
Metodi e attributi di classe
public class Shape {
static Screen screen=new Screen(); // si noti l’inizializzazione
static void setScreen(Screen s) {screen=s;}
void show(Screen s) {setScreen(s);}

public static void main(String[] args) {


Shape.setScreen(new Screen()); // corretto
Shape.show(); // errato, show e’ un metodo normale
Shape s1=new Shape(), s2=new Shape();
Screen s=new Screen();
s1.setScreen(s);
// corretto, si possono chiamare metodi static su oggetti

// in questo punto s2.screen==s1.screen==s


}
}

48
Ancora sui costruttori
• È possibile invocare un costruttore dall’interno di un altro tramite la
notazione
– this(<elenco di parametri attuali>);
– this deve essere la prima istruzione
import java.util.Calendar;

public Data(int g, int m, int a){


if (dataValida(g,m,a)) { // dataValida e' un metodo statico
giorno = g;
mese = m;
anno = a;
}
else ...
}

public Data(int g, int m) { //giorno e mese + anno di default


this(g,m,1900);
}
49
Attributi costanti
• È possibile definire attributi costanti tramite la
notazione:
– final <definizione di attributo>=<valore>
public class Automobile {
int colore;
final int BLU=0, GIALLO=1; // e altri
void dipingi(int colore) {this.colore=colore;}

public static void main(String[] args) {


Automobile a=new Automobile();
a.BLU=128; // errato
System.out.println("BLU="+a.BLU); // corretto
}
}
50
Overloading di metodi
• All’interno di una stessa classe possono esservi più metodi
con lo stesso nome purché si distinguano per numero e/o
tipo dei parametri
– Attenzione! Il tipo del valore restituito non basta a distinguere
due metodi

• In Java l’intestazione di un metodo comprende il numero, il


tipo e la posizione dei parametri; non include il tipo del
valore restituito
– Metodi overloaded devono avere intestazioni diverse

• Utile per definire funzioni con codice differente ma con


effetti simili su tipi diversi

51
Esempio
public class Prova {
public int max(int a, int b, int c) {...}
public double max(double a, double b) {...}
public int max(int a, int b) {...}
}

public static void main(String[] args){


Prova p = new Prova();

p.max(2,3,5);
p.max(2.3, 3.14);
p.max(2,3);
}
Ogni volta verrà chiamato
il metodo “giusto”!

52
Reference e operatore ==
• L’operatore di confronto == confronta i valori dei riferimenti
(identità) e non gli oggetti (stato)!
d1 xxx 1 giorno
Data d1 = new Data(1,12,2001); 12 mese
Data d2= new Data(1,12,2001); 2001 anno
if (d1==d2) {
Falso! I riferimenti 1 giorno
... sono diversi anche d2 yyy
se lo stato degli 12 mese
d1=d2;
oggetti è lo stesso! anno
2001

Il confronto d1==d2
diventa vero, abbiamo
creato un alias quindi i
riferimenti sono uguali

53
Confronto di uguaglianza
• Metodo equals() consente di verificare se due
oggetti sono uguali, nel senso che hanno lo stesso
valore dello stato
– per String: contengono la stessa sequenza di caratteri

• Dice se due oggetti sono equivalenti


– Che cosa ciò esattamente significhi dipende dal tipo
dell'oggetto
– Per esempio, due insiemi sono equivalenti se
contengono gli stessi elementi, indipendentemente
dall'ordine di inserimento

54
Uso

String stringa1 = "Luciano”;


String stringa2 = "Giovanni”;

stringa1.equals(stringa2); //false
String b = new String("Ciao");
String c = new String("Ciao");
if (b.equals(c)); //true

55
String
• Le stringhe sono immutabili
– Non si possono aggiungere o togliere caratteri a una stringa, ma occorre
costruirne una nuova

• Costruttori
– String()
– String(String s)

• Operatore di concatenamento +

• Alcuni metodi pubblici


– int length() restituisce la lunghezza di una stringa
– char charAt(int index) restituisce il char alla posizione index
• Il primo ha posizione 0
– String substring(int beginIndex) (parte da 0)

56
Esempio
public class ProvaString {
public static void main (String[] args) {
String a = new String(); // a e’ una ref a stringa vuota
String b = new String("Ciao"); // b e’ una ref a “Ciao”:
// abbreviazione: String b = “Ciao”;
String c = new String(b); // Ora c e’ copia di b
String d = b; // d e b sono alias
System.out.println(b + " " + c + " " + d);
}
}

• L’assegnamento d=b è un assegnamento dei riferimenti


– Non si copia l’oggetto!

57
Enumerazioni
• Si possono dichiarare tipi enumerati, per modellare insiemi
con cardinalità ridotta

enum Size {SMALL, MEDIUM, LARGE, X_LARGE};

Size s = Size.MEDIUM;

• Size è una vera classe: ha esattamente quattro istanze


– Non se ne possono costruire altre
– Non c’è bisogno di usare equals per confrontare i valori, basta
==
– s può essere solo null o uno dei valori enumerati

58
Enumerazioni
• A una classe enumerata si possono aggiungere costruttore, metodi
e attributi
– Permettono di associare qualsiasi informazione alle costanti
enumerate
– I costruttori sono invocati solo quando vengono “costruite” le costanti
– Non possiamo costruire oggetti generici da classi enumerazioni!
public enum Size {
SMALL("S"), MEDIUM("M"), LARGE("L"), X_LARGE("XL");
private String abbreviation;

private Size(String abbreviation) {


this.abbreviation=abbreviation;
}

public String getAbbreviation(){


return abbreviation;
}
}
59
Enumerazioni
• Tutte le classi enumerate offrono i seguenti metodi
– static Enum valueOf(String name)
• Restituisce la costante enumerata della classe indicata che ha quel nome
– String toString()
• Restituisce il nome della costante enumerata
– Size[] valori = Size.values();
• Restituisce un array contenente tutti i valori della classe

import java.util.Scanner;

public class ProvaString {


public static void main (String[] args) {
Scanner in = new Scanner(System.in);
String str = in.next();
Size size = Enum.valueOf(Size.class, str);
System.out.println(size.toString());
}
}
60
Altri metodi
• name()
– Restituisce il nome della costante enumerativa

• ordinal()
– Restituisce la posizione (partendo da 0)

• compareTo(…)
– Confronta l’oggetto corrente C con la variabile enumerativa passata
come parametro e restituisce
• Un valore negativo se C è minore del parametro
• Zero se C è uguale al parametro
• Un valore positivo se C è maggiore del parametro

• …

61
Loop generalizzato
enum Color {Red, White, Blue}

for (Color c: Color.values()) { . . . }

62
Esempio
• Un esempio più ricco: pianeti del sistema solare, associati alla propria
massa e raggio; si può calcolare il peso di un oggetto su ogni pianeta

public enum Planet {


MERCURY (3.303e+23, 2.4397e6), VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6), MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7), SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7), NEPTUNE (1.024e+26, 2.4746e7),
PLUTO (1.27e+22, 1.137e6);

private final double mass; // in kilograms


private final double radius; // in meters

Planet(double mass, double radius) {


this.mass = mass; this.radius = radius;
}

public double mass() {return mass;}


public double radius() {return radius;}

// universal gravitational constant (m^3 kg^-1 s^-2)


public static final double G = 6.67300E-11;

public double surfaceGravity() { return G * mass / (radius * radius); }


public double surfaceWeight(double otherMass) { return otherMass*surfaceGravity(); }
}

63
Esempio
• A partire dal peso di un corpo sulla terra, calcola e stampa il
peso su tutti gli altri pianeti
public static void main(String[] args) {
double earthWeight = Double.parseDouble(args[0]);
double mass = earthWeight/EARTH.surfaceGravity();
for (Planet p : Planet.values())
System.out.printf("Your weight on %s is %f\n",
p,p.surfaceWeight(mass));
}

64
Tipi riferimento per i tipi primitivi
• I tipi primitivi sono comodi, ma a volte si
preferirebbe usarli come riferimento, per
omogeneità

• Java fornisce classi predefinite


– Integer, Character, Float, Long, Short, Double (sono in
java.lang)
– Un oggetto Integer contiene un int, ma viene
inizializzato solo con i costruttori
• Il tipo Integer è immutabile

65
Esempi

Integer i; // qui i vale null!


i = new Integer(5); // i e’ un rif. a oggetto che
contiene 5
Integer x = i; // sharing: x e i stesso oggetto
int y = x.intValue(); // vecchio modo
i = y; // boxing automatico
y = i; // unboxing automatico
i = 3; // come sopra

66
“Catene puntate”
• Un oggetto può avere attributi che sono
ancora oggetti o metodi che restituiscono
oggetti: accesso a metodi e attributi avviene a
“catena”

67
Esempi
• System.out.println();
– out è attributo pubblico (statico) di classe System
– La classe di out fornisce il metodo println()

• String b = new String("Ciao");


String a = b.substring(1).substring(2);
System.out.println(a); //che oggetto e’ a?

– Operatore . è associativo a sinistra


(b.substring(1)).substring(2);
(System.out).println();

68
EREDITARIETÀ

69
Ereditarietà
• È possibile stabilire una relazione “sottoclasse_di” (Í)
fra le classi di un programma Java
– Relazione d’ordine parziale (riflessiva e transitiva)

public class B extends A {…}

• A classe base, o antenato, o padre, o superclasse, …

• B classe derivata, o discendente, o figlio, o erede, o


sottoclasse, …

70
Relazione di ereditarietà
• La sottoclasse eredita tutta l’implementazione
(attributi e metodi) della superclasse
– Gli attributi e metodi della superclasse sono
implicitamente definiti anche nella sottoclasse (ma
con alcune differenze che vedremo fra poco)

• Una sottoclasse può aggiungere nuovi attributi e


metodi ma anche ridefinire i metodi delle sue
superclassi
– Lasciando invariato numero e tipo dei parametri
– Viene detto overriding

71
Un semplice esempio
public class Automobile {
private String modello;
private boolean accesa;
public Automobile(String modello) {
this.modello=modello;
this.accesa = false;
}
public void accendi() {accesa=true;}
public boolean puoPartire() {return accesa;}
}

public class AutomobileElettrica extends Automobile {


private boolean batterieCariche;
public void ricarica() {batterieCariche=true;}
...
}
}

72
Gerarchia a più livelli
pixel
width
color
scale()
Figure rotate()
draw()

perimeter
area ClosedFigure OpenFigure

numberOfSides
Polygon Ellipse

Rectangle

73
Overriding
• Una sottoclasse può ridefinire l’implementazione di un metodo
– L’intestazione del metodo non deve cambiare
– L’intestazione non include il tipo restituito, che quindi può cambiare a
patto che specializzi il tipo del metodo ridefinito
• Regola della covarianza
public class AutomobileElettrica extends Automobile {
private boolean batterieCariche;

public void ricarica() {


batterieCariche = true;
}

public void accendi() {// OVERRIDING
// accensione auto elettrica e’ diversa da quella
// di auto a benzina … la reimplementiamo
}
}
74
Pseudo variabile super
• All’interno di un metodo della sottoclasse ci si
può riferire ai metodi della superclasse
– super.<nome metodo>(<lista par. attuali>)
public class AutomobileElettrica extends Automobile{

private boolean batterieCariche;


public void ricarica() {batterieCariche=true;}

public void accendi() {// OVERRIDING


if(batterieCariche) super.accendi();
else System.out.println("Batterie scariche");
}
}

75
Costruttori
• I costruttori non sono ereditati perché occorre
inizializzare anche i nuovi attributi

• Per inizializzare gli attributi private ereditati,


all’interno di un costruttore è possibile
richiamare il costruttore della superclasse
tramite super
– super(<lista di par. attuali>)
posta come prima istruzione del costruttore

76
Costruttori
• Se il programmatore non chiama
esplicitamente un costruttore della
superclasse, il compilatore inserisce
automaticamente il codice che invoca il
costruttore di default della superclasse
– ... che potrebbe non esistere!
public AutomobileElettrica(String modello) {
super(modello); //qui inizializza modello e accesa
batterieCariche=false;
}

77
Object
• In mancanza di un’indicazione differente, una
classe Java estende la classe Object

• La classe Object fornisce alcuni metodi di


default tra i quali:
– public boolean equals(Object);
– public String toString();
– public int hashCode();

78
INFORMATION HIDING
Package
• Le classi sono raggruppate in package
– Un package raggruppa classi definendo regole di
visibilità

• Se una classe C è visibile nel package pA, ma è


dichiarata nel package pB, questa viene denotata
come pB.C
– Quindi si possono usare liberamente gli stessi nomi in
package diversi, senza generare confusione

80
Compilation unit
• Un file che contiene la dichiarazione di una o più
classi (o interfacce)
– Una sola dichiarata pubblica (public class) e avente lo
stesso nome del file

• C’è al più un solo metodo main

• Si può specificare il package di appartenenza (lo


stesso per tutte)
– Se non si specifica, si assume un package senza nome
di default

81
Package
• Una directory che contiene una o più compilation unit

• Introduce un nuovo ambito di visibilità dei nomi


– Unit con lo stesso nome possono stare in package diversi

• Contiene un insieme di classi pubbliche ed un insieme


di classi private al package (“friendly”)

• Le classi pubbliche si possono “importare” in altri


package

82
Visibilità delle classi
• public
– Sono visibili a tutti con import del package
– Il file deve avere lo stesso nome
– Al più una public class per ogni file

• “friendly”
– Sono visibili solo all’interno dello stesso
package/compilation unit
– Possono stare in un file con altre classi

83
Esempio
comp unit 2

package myFigs.planar;
comp unit 1
public class DrawableElement {
package myTools.text; ...
public class TextComponent { }
. . .Zzz z;… class xxx {
} ...
}
comp unit 3

package myTools.text;
public class yyy {
...
}
class Zzz{
...
}
84
Visibilità di attributi e metodi
• Attributi e metodi di una classe vengono sempre
ereditati e possono essere
1 – public
• Sono visibili a tutti
4 – private
• Sono visibili solo all’interno della classe
• Non sono visibili nelle sottoclassi
2– protected
• Sono visibili alle classi nello stesso package
• Sono visibili anche alle sottoclassi
3– “friendly”
• Sono visibili alle classi nello stesso package
• Sono visibili solo alle sottoclassi nello stesso package

85
Information hiding
• Una classe/attributo/metodo public è una promessa agli utilizzatori
della classe
– Sarà disponibile e non cambierà, perlomeno dal punto di vista degli
utilizzatori della classe

• La promessa è molto vincolante


– Meglio promettere poco!

• Tutte le proprietà per cui ci si vuole mantenere la possibilità di


modifica o eliminazione devono essere private
– Al massimo, ma solo se indispensabile, friendly
• Ad esempio per le proprietà “helper” di una classe
• Usare comunque con attenzione: se un attributo è friendly e qualcuno lo usa
non possiamo più cambiarlo!

86
Information hiding
• È fortemente consigliato che gli attributi di una classe
public siano private o friendly
– Usare metodi per accedervi!

• I metodi che possono essere usati dagli utilizzatori


“esterni” della classe dovrebbero essere public
– Gli attributi friendly sono usati solo quando le classi
all'interno dello stesso package devono avere accesso
privilegiato
– Esempio: una classe Lista deve usare una classe Nodo che
implementa i nodi della Lista: è utile che Lista possa
accedere ai campi di Nodo, ma gli utilizzatori di Lista non
devono potere accedere a Nodo

87
Accesso ai membri private
• Le sottoclassi non possono accedere agli
attributi (e metodi) private delle superclassi!

• Quindi è sbagliato scrivere:


public void accendi() {
if (batterieCariche) accesa = true;
}

• … perché accesa è private nella superclasse!

88
POLIMORFISMO
Ereditarietà
• Una classe definisce un tipo

• Una sottoclasse (transitivamente) definisce un sottotipo

• Un oggetto del sottotipo è sostituibile a un oggetto del tipo

• Si distingue tra
– Tipo statico: il tipo dichiarato
– Tipo dinamico (o attuale): il tipo dell'oggetto attualmente
assegnato

90
Ereditarietà
• Java garantisce che la sostituibilità non
comprometta la type safety

• Il compilatore verifica che ogni oggetto venga


manipolato correttamente in base al tipo statico

• Il linguaggio garantisce che a run time non


sorgano errori se invece si opera su un oggetto il
cui tipo dinamico è un sottotipo del tipo statico

91
Esempio
public class UsaAutomobile {
public static void partenza(Automobile p) {
if (p.puoPartire())
p.accendi();
}

public static void main(String args[]) {


// Legale!!
Automobile myCar = new AutomobileElettrica("T");
// Funziona anche con AutomobileElettrica
partenza(myCar);
}
}

92
Polimorfismo
• L'esempio precedente è un caso di polimorfismo

• Polimorfismo è la capacità per un elemento sintattico di


riferirsi a elementi di diverso tipo

• In Java una variabile di un tipo riferimento T può riferirsi ad


un qualsiasi oggetto il cui tipo sia T o un sottotipo di T

• Similmente un parametro formale di un tipo riferimento T


può riferirsi a parametri attuali il cui tipo sia T o un
sottotipo di T esempio partenza(myCar)

93
Tipo dinamico e tipo statico
• Il tipo statico è quello definito dalla dichiarazione

Automobile myCar;

• Il tipo dinamico è definito dal costruttore usato per definirlo

AutomobileElettrica yourCar = new AutomobileElettrica();

• In Java, il tipo dinamico può essere sottotipo del tipo statico


Automobile myCar = new Automobile();
AutomobileElettrica yourCar = new AutomobileElettrica();
myCar = yourCar;

94
Assegnamento polimorfico
• A un oggetto di tipo statico T si può sempre assegnare un oggetto il
cui tipo statico S è sottotipo di T (ma non viceversa)

• Questo consente che il tipo dinamico possa essere sottotipo di


quello statico

Automobile myCar = new AutomobileElettrica();

• Il compilatore verifica che ogni oggetto venga manipolato


correttamente solo in base al tipo statico

• La regola precedente garantisce che a runtime non sorgano errori


se si opera su un oggetto il cui tipo dinamico è un sottotipo del tipo
statico

95
Uso delle classi dell’esempio
AutomobileElettrica yourCar = new AutomobileElettrica("E1");
Automobile myCar = new Automobile("Ford");
myCar = yourCar;

myCar xxx Ford modello


false accesa

yourCar xxx El modello


false accesa
true batterieCariche

96
Chiamata di un metodo
Automobile myCar = new AutomobileElettrica();

myCar.puoPartire(); // OK, chiama metodo di Automobile

myCar.accendi(); // OK, chiama metodo di AutomobileElettrica

myCar.ricarica(); // KO, ricarica non e’ metodo di Automobile

AutomobileElettrica yourCar = new AutomobileElettrica ();

yourCar.ricarica(); // OK, chiama metodo di AutomobileElettrica

97
Polimorfismo e binding dinamico
• In Java, a fronte della invocazione x.f(x1,...,xn),
l’implementazione scelta per il metodo f
dipende dal tipo dinamico di x e non dal suo
tipo statico

• Il legame (binding) tra il metodo invocato e il


metodo attivato è dinamico, dipende dal tipo
attuale dell'oggetto

98
Esempio
public class UsaAutomobile {

public static void partenza(Automobile a) {


a.accendi(); //ridefinito in AutomobileElettrica
}

public static void main(String args[]) {


Automobile a1 = new Automobile("Ford");
Automobile a2 = new AutomobileElettrica("T");
a1.accendi();
// a run-time chiama implementazione di Automobile
a2.accendi();
// a run-time chiama implementazione di AutomobileElettrica
partenza(a2);
// solo a run time si puo’ conoscere il tipo effettivo
}
}

99
Binding dinamico
public static void partenza(Automobile a) {
a.accendi();
}

• Quale implementazione del metodo accendi() chiamare


– Quella di Automobile o quella di AutomobileElettrica?
– Il tipo effettivo del parametro a è noto solo quando accendi
viene chiamato, cioè a runtime

• Il compilatore non genera il codice per eseguire il metodo,


ma genera il codice che cerca l’implementazione giusta in
base al tipo effettivo di a e la esegue
– Questo si chiama dispatching, che in Java avviene in maniera
dinamica

100
Overloading e overriding
• Overriding non va confuso con overloading
public class Punto2D{
public float distanza(Punto2D p){…}
}
public class Punto3D extends Punto2D {
public float distanza(Punto3D p){…} //OVERLOADING!!!
}

– Il metodo distanza di Punto3D ha un’intestazione


diversa da quella di distanza dichiarato in Punto2D:
Punto2D p = new Punto3D();
p.distanza(p); //chiama Punto2D.distanza(Punto2D)

– Non è overriding à non si applica binding dinamico

101
Regola per chiamata metodi
• Il compilatore, quando trova una chiamata di un
metodo x.m(p) risolve staticamente l’overloading,
individuando la signature del metodo chiamato in
base al tipo statico P del parametro attuale p e al
tipo statico X di x

• Il binding dinamico si applica a run-time: il codice


sceglie a runtime il metodo “più vicino” tra quelli
che hanno il prototipo X.m(P) stabilito
staticamente
– Risale la gerarchia per cercare il metodo più vicino

102
Esempio
public class Punto2D{ public float distanza(Punto2D p) {…} }
public class Punto3D extends Punto2D {public float distanza(Punto3D p) {…} }

public void static void main(String[] args){


Punto2D p1,p2;
Punto3D p3;

p1 = new Punto2D(3,7);
p2 = new Punto3D(3,7,4);
System.out.println(p1.distanza(p2)); // Metodo di Punto2D
System.out.println(p2.distanza(p1)); // Punto2D
p3 = new Punto3D(6,7,5);
System.out.println(p2.distanza(p3)); // Punto2D
System.out.println(p3.distanza(p1)); // Punto2D
System.out.println(p1.distanza(p3)); // Punto2D

Punto3D p4 = new Punto3D(6,1, 5);


System.out.println(p3.distanza(p4)); // Punto3D
}

103
Classi e metodi astratti
• Un metodo astratto è un metodo per il quale non viene
specificata alcuna implementazione

• Una classe astratta può contenere uno o più metodi


astratti

• Non è possibile creare istanze di una classe astratta

• Le classi astratte sono molto utili per introdurre delle


astrazioni di alto livello

104
Classi e metodi astratti
abstract class Shape {
static Screen screen = new Screen();
Shape(){}
abstract void show();
}

class Circle extends Shape {


void show() {

}
}

Shape s = new Shape(); //ERRATO


Circle c = new Circle(); //CORRETTO
Shape s = new Circle(); //CORRETTO

105
Classi e metodi final
• Se vogliamo impedire che sia possibile creare
sottoclassi di una certa classe la definiremo
final final class C {...}
class C1 extends C //ERRATO

• Similmente, se vogliamo impedire l’overriding


di un metodo dobbiamo definirlo final
class C { final void f(){ … }
class C1 extends C {
void f() { … } //ERRATO
}

106
I limiti dell’ereditarietà semplice
• L’ereditarietà semplice non permette la
descrizione di numerose situazioni reali
– Supponiamo di avere una classe Giocattolo ed una
classe Automobile
– In assenza di ereditarietà multipla non posso definire
la classe AutomobileGiocattolo

• La soluzione di Java
– Distingue tra una gerarchia di ereditarietà (semplice)
ed una gerarchia di implementazione (multipla)
introducendo il costrutto interface

107
Interfacce
• Un’interfaccia è come una classe che può avere solo attributi costanti e i
cui metodi sono tutti pubblici e astratti

• Sintassi
interface <nome> {
<lista di definizione di attributi costanti e metodi privi di corpo>
}

• Gli attributi dichiarati in un’interfaccia sono


– Visibili alla classe che la implementa
– Immutabili (dichiarati come static final)

public interface Scalable {


int SMALL=0, MEDIUM=1, BIG=2; //static e final
void setScale(int size);
}

108
Interfacce ed ereditarietà
• Una interfaccia può ereditare da una o più
interfacce

interface <nome> extends <nome1>, … ,<nomen> {


...
}

109
La gerarchia di implementazione
• Una classe può implementare una o più
interfacce, ma estendere al più una classe

• Se la classe non è astratta deve fornire


un’implementazione per tutti i metodi
presenti nelle interfacce che implementa …

• … altrimenti la classe è astratta


110
Classi astratte e interfacce
• Classi astratte
– Possono avere metodi implementati e non

• Interfacce
– Possono avere solo metodi non implementati

• Classi concrete
– Hanno tutti i metodi implementati
111
Classe astratta
abstract class TwoDimFigure {
public abstract double area();
public abstract double perimeter();
public abstract void printInfo();
public void setOutlineColor(Color cc) {
// code to set the color
}
public void setInsideColor(Color cc) {
// code to set the color
}
}

112
Interfaccia
public interface Shape {
public String baseclass="shape";
public void draw();
}

public class Circle implements Shape {


public void draw() {
System.out.println("Drawing Circle here");
}
}

public class Main {


public static void main(String[] args) {
Shape circleshape = new Circle();
circleshape.draw();
}
}
113
I tre principi
• Incapsulamento
– Gli oggetti nascondono il
loro stato e parte del loro
comportamento

• Ereditarietà auto Super classe

– Ogni sottoclasse eredità


tutte le proprietà manuale automatica Sottoclassi
della/delle superclassi

• Polimorfismo disegna() disegna()


– Stessa interfaccia anche
per tipi di dati diversi

114
CONVERSIONI AUTOMATICHE
DI TIPO
Promozioni
byte -> short, int, long, float, double
short -> int, long, float, double
int -> long, float, double
long -> float or double
float -> double
char -> int, long, float, double

116
Conversioni forzate: casting
• È possibile forzare una conversione di tipo attraverso
l’operatore di casting:
– (<tipo>)<espressione>

• Tra tipi primitivi sono consentite le seguenti conversioni


forzate (quando possibile e con perdita di informazione)
– short -> byte, char
– char -> byte, short
– int -> byte, short, char
– long -> byte, short, char, int
– float -> byte, short, char, int, long
– double -> byte, short, char, int, long, float
– byte -> char

117
Casting in generale
• È possibile forzare esplicitamente la conversione
da un tipo riferimento T ad un sottotipo T1

• Purché il tipo dinamico dell’espressione che


convertiamo sia un sottotipo di T1
Animale a = …;
Gatto mao = …; // eredita da Animale
a = mao; // OK assegnazione polimorfica
mao = (Gatto)a; // corretto (casting) perche’ a e’ un gatto

118
instanceof
• Per evitare errori runtime e stabilire qual è il
tipo dinamico di un oggetto si può usare
l’operatore instanceof
Object a;
int i = System.in.read();
if (i>0) a = new String();
else a = new Integer(5);
if (a instanceof String) return a.equals("abcd")

119
instanceof e equals
public class Data {
private int giorno;
private int mese;
private int anno;

public int leggiGiorno(){…}


public boolean equals(Object o) {
if (!(o instanceof Data)) return false;
Data d= (Data) o;
return (giorno==d.giorno &&
mese == d.mese && anno == d.anno);
}
}

120
GESTIONE DELLE ECCEZIONI
Situazioni eccezionali
• Un metodo deve poter segnalare
l’impossibilità di produrre un risultato
significativo o la propria terminazione
scorretta
– Apertura di un file (ma il file non esiste)
– Calcolo della radice quadrata di un numero (ma il
numero è negativo)

122
Situazioni eccezionali
• Soluzioni
– Terminazione del programma
– Restituire un valore convenzionale che rappresenti l’errore
• Può non essere fattibile
• Il chiamante potrebbe dimenticarsi di controllare
– Portare il programma in uno stato scorretto
• Si usa una variabile globale ERROR
• …ma il chiamante deve ricordarsi di controllare!
– Usare un metodo predefinito per la gestione degli errori
• Ad esempio, si chiama un metodo ERROR(...)
– Centralizza la gestione degli errori (che spetterebbe al chiamante)
– Rende difficoltoso il ripristino

123
Gestione esplicita delle eccezioni
• Una procedura può terminare normalmente
– Con un risultato valido o “sollevando” un’eccezione

• Un’eccezione è un oggetto speciale restituito dal metodo


– Le eccezioni vengono segnalate al chiamante che può gestirle
nella maniera più opportuna
– Le eccezioni hanno un tipo (Classe)
• Esempi: DivisionByZeroException, NullPointerException
– Le eccezioni possono contenere dati che danno indicazioni sul
problema incontrato
– Le eccezioni possono anche essere definite dall’utente
(personalizzazione)

124
Eccezioni in Java
• Un'eccezione può essere catturata e gestita attraverso il
costrutto try/catch
– try {<blocco>} catch(ClasseEccezione e) {<codice di gestione>}

try {
x = x/y;
} catch (DivisionByZeroException e) {
// l’oggetto e è l’oggetto eccezione
// qui c’è il codice per gestire l’eccezione
// qui e’ possibile usare e
}

// istruzione successiva, da eseguire se non c’e’ stata


// eccezione o se catch e’ riuscito a “recuperare”

125
Più rami catch
• Un ramo catch(Ex e) può gestire un’eccezione di tipo T se T
è di tipo Ex o T è un sottotipo di Ex
• Più clausole catch possono seguire lo stesso blocco try
– Ciascuna cattura l’eccezione del proprio tipo
public void faiQualcosa() {
try {
leggiFile();
} catch(FileInesistenteException fi) {
System.out.println("Ooops! Il file " +
fi.getNomeFile() + " non esiste!");
} catch(FileDanneggiatoException fd) {
System.out.println("Ooops! Il file " +
fd.getNomeFile() + " ha dati scorretti!");
}
}

126
Propagazione delle eccezioni
• Si termina l’esecuzione del blocco di codice in cui si è verificata l’eccezione

• Se il blocco di codice corrente è un blocco try/catch ed esiste un catch in


grado di gestire l’eccezione, si passa il controllo al primo di tali rami catch
e, completato questo, alla prima istruzione dopo il blocco, altrimenti

• Si risalgono eventuali blocchi di codice più esterni fino a trovare un blocco


try/catch che contenga un ramo catch che sia in grado di gestire
l’eccezione, altrimenti

• L’eccezione viene propagata al chiamante, fino a che si trova un blocco


try/catch che gestisce l’eccezione

• Se tale blocco non si trova, il programma termina

127
Flusso in presenza di eccezioni
main proc2
• Flusso: block1
– main attivato block2
block3
– blocco 1
– blocco 2 call to proc1 block4
– proc1 invocata
– proc2 invocata throw exception
– blocco 3
proc1
– blocco 4 block5
– ... eccezione!
call to proc2
– propagazione
dell’eccezione!

128
Il ramo finally
• Un blocco try/catch può avere un ramo finally in
aggiunta a uno o più rami catch

• Il ramo finally è comunque eseguito


– Sia che all’interno del blocco try non vengano
sollevate eccezioni
– Sia che all’interno del ramo try vengano sollevate
eccezioni gestite da un catch
• In tal caso il ramo finally viene eseguito dopo il ramo catch
che gestisce l’eccezione
– Sia che all’interno del blocco try vengano sollevate
eccezioni non gestite da un catch
129
Esempio

public class Prova {


public void read(String fileName) {
try {
FileInputStream f=new FileInputStream(fileName);
... // use f
} catch(IOException ex) {
...
} finally {
f.close();
}
}
}

130
Metodi con eccezioni
• Il fatto che un metodo possa terminare sollevando un’eccezione è
dichiarato nella sua interfaccia per mezzo della clausola throws
– Per segnalare un comportamento anomalo incontrato durante
l’esecuzione di un’istruzione
public int leggiInteroDaInput() throws IOException

– Notificare che una precondizione su un’operazione è stata violata

public int fact(int n) throws NegativeException

– Restituire un valore convenzionale

public int search(int[] a, int x) throws NullPointerException,


NotFoundException

131
Sollevare eccezioni
• Per sollevare esplicitamente un’eccezione, si usa il
comando throw, seguito dall’oggetto (del tipo
dell’eccezione) da “lanciare” al chiamante

• Informalmente throw
– Termina l’esecuzione del blocco di codice che lo contiene,
generando un'eccezione del tipo specificato
public int fact(int n) throws NegativeException {
if (n<0) throw new NegativeException();
else if (n==0 || n==1) return 1;
else return (n*fact(n-1));
}

132
Tipi di eccezioni
• Eccezioni definite tramite
classi, sottotipo del tipo Throwable
Throwable
Error Exception

• Esistono due tipi di


eccezioni RuntimeException (checked
– Checked exceptions)
• Sottotipo di Exception
– Unchecked (unchecked exceptions)

• Sottotipo di RuntimeException

133
Eccezioni checked
• Devono essere dichiarate dai metodi che possono
sollevarle (altrimenti si ha un errore in compilazione)

• Quando un metodo M1 invoca un altro metodo M2 che


può sollevare un’eccezione di tipo Ex (checked), una
delle due deve essere vera
– L’invocazione di M2 in M1 avviene internamente ad un
blocco try/catch che gestisce eccezioni di tipo Ex
• Quindi, M1 gestisce l’eventuale eccezione
– Il tipo Ex (o un suo super-tipo) fa parte delle eccezioni
dichiarate nella clausola throws del metodo M1
• Quindi, M1 propaga l’eventuale eccezione

134
Eccezioni unchecked
• Possono propagarsi senza essere dichiarate in
alcuna intestazione di metodo e senza essere
gestite da nessun blocco try/catch

• Può essere meglio includerle comunque in


throws, per renderne esplicita la presenza (ma
per il compilatore è irrilevante)

135
Definizione di nuove eccezioni
• Gli oggetti di un qualunque tipo T definito
dall’utente possono essere usati per sollevare
e propagare eccezioni se T è definito come
sotto-tipo della classe Exception o
RuntimeException

• La definizione della classe che descrive


un’eccezione non differisce dalla definizione di
una qualsiasi classe definita dall’utente

136
Definizione di nuove eccezioni
• Può possedere attributi e metodi propri usati per
fornire informazioni aggiuntive al gestore
dell’eccezione
public class NewKindOfException extends Exception {
public NewKindOfException(){ super(); }
public NewKindOfException(String s){ super(s); }
}

• I due costruttori richiamano semplicemente i


costruttori di Exception
throw new NewKindOfException("problema!!!")
try{ … }
catch(NewKindOfException ecc) {
String s = ecc.toString();
System.out.println(s);
}
137
Eccezioni con un costruttore
public class ProvaEcc {
public static void main(String[] args) {
int g,m,a;
Data d;
... // leggi g, m, a
try {d=new Data(g,m,a);}
catch(DataIllegaleException e) {
System.out.println("Inserita una data illegale");
System.exit(-1);
}}}

public class Data {


private int giorno, mese, anno;
private boolean corretta(int g,int m,int a) {...}

public Data(int g, int m, int a) throws DataIllegaleException {


if(!corretta(g,m,a)) throw new DataIllegaleException();
giorno=g; mese=m; anno=a;
}}

class DataIllegaleException extends Exception {};

138
Eccezioni unchecked
• Il loro uso dovrebbe essere limitato ai casi in cui
– Si tratta di eccezioni di tipo aritmetico/logico
– C’è un modo conveniente e poco costoso di evitarle
• Ad esempio, le eccezioni aritmetiche: posso sempre, se mi
serve, controllare prima di eseguire il calcolo
• Ad esempio, per gli array, le eccezioni di tipo
OutOfBoundException possono essere evitate controllando
in anticipo il valore dell’attributo length dell’array
– L’eccezione è usata solo in un contesto ristretto
– Meglio dichiararle comunque in throws quando un
metodo le può lanciare

139
Masking
• Dopo la gestione dell’eccezione, l’esecuzione continua seguendo il
normale flusso del programma
– L’eccezione viene gestita e non si propaga al chiamante

• Eccezione usata per verificare una condizione


public static boolean sorted (int[] a) {
int prev;
try {prev=a[0];} // lancia eccezione se array e’ vuoto
// (era meglio check diretto su a)
catch (IndexOutOfBoundsException e){return true;}
for (int i=1; i<a.length; i++) {
if (prev <= a[i]) prev=a[i];
else return false;
}
return true;
}
140
Consigli utili
• Aggiungere ai dati correlati con l’eccezione l’indicazione del metodo
che l’ha sollevata

• Nel caso in cui la gestione di un’eccezione comporti un’ulteriore


eccezione (reflecting), conservare le informazioni da una eccezione
alla successiva

• Sebbene sia possibile scegliere liberamente i nomi delle nuove


eccezioni definite, è buona convenzione farli terminare con la
parola Exception

• Può essere talvolta utile prevedere un package contenente tutte le


nuove eccezioni definite
– A volte invece conviene definire eccezioni come classi private …

141
COLLECTIONS

142
List
• Le liste (List) sono un tipo di dato che
rappresenta liste dinamiche
– Diverse implementazioni
• ArrayList, LinkedList
• Diverso costo di inserimento, cancellazione, …
– Da Java 5 sono parametriche (generiche) rispetto
al tipo degli oggetti contenuti
• Es. List<Integer>, List<String>, …

143
List
import java.util.ArrayList;

List<Person> team = new ArrayList<Person>();

team.add(new Person("Bob"));
team.add(new Person("Joe"));

team.size()

144
List
• I metodi add(indice, oggetto) e remove(indice)
aggiungono e tolgono un elemento nella posizione
indicata, alterando la lunghezza della List
team.add(1, new Person("Sue")); //ora ci sono Mary, Sue, Joe
team.remove(0); // rimuove Mary, ora ci sono Sue e Joe

• I metodi get e set permettono di accedere ad elementi


in una specifica posizione (fanno riferimento ad indici
che iniziano da 0)
team.get(1); //restituisce la Person di nome Joe
team.set(0, new Person("Mary")); //sostituisce Mary a Bob

145
List
• Il metodo set non deve essere usato per inserire
un elemento in una posizione che non c’è
team.set(4, new Person("Jack")); // scorretto

• … ma solo per sostituire un oggetto già presente


in quella posizione
for (Person p: team){
// fa qualcosa con la persona p
}

146
List
• Il costo dipende dall’implementazione
specifica

• add e remove sono operazioni “costose” in


ArrayList perché comportano la “traslazione”
di segmenti di array

147
Collections
• In generale, List è un particolare tipo di
collezione di elementi offerto dalla libreria
standard di Java

• Esiste una gerarchia di collezioni che è


fondamentale conoscere per scegliere la
struttura dati più adatta per uno specifico
problema

148
Collections

149
Maps

150
Genericità e sottotipi
• Alcune cose sembrano andare contro
l’intuizione
List<String> ls = new ArrayList<String>();

List<Object> lo = ls; // ERRORE a compile time

lo.add(new Object());

String s = ls.get(0);
// ERRORE tenta di assegnare Object a String!

151
Qualche considerazione
• Una lista di String è una lista di Object?
– NO! Altrimenti tramite una variabile lista di Object
si potrebbe inserire un Object in una lista di String
e quando si tenta di estrarre, tramite una variabile
lista di String, un oggetto String, si verifica un
errore
– In generale, se Gen<T> è una classe generica e se
ClassB è sottoclasse di ClassA allora Gen<ClassB>
non è sottoclasse di Gen<ClassA>

152
Come funzionano
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) { c.add(o); }
}

• Per rendere corretto l’inserimento c.add(o) dobbiamo chiamare


fromArrayToCollection con parametri in cui il tipo dell’elemento
della collezione sia supertipo di quello dell’array

• Non occorre indicare esplicitamente i parametri attuali in


corrispondenza ai parametri formali di un metodo generico
– Ci pensa il compilatore a inferire il tipo del parametro attuale
scegliendo il tipo più specifico …
– … cioè il minimo, nell’ordinamento tra tipi definito dalla gerarchia di
ereditarietà, tra tutti quelli che rendono legale l’invocazione del
metodo generico

153
Esempi

String[] sa = new String[100];

Collection<String> cs = new ArrayList<String>();


fromArrayToCollection(sa, cs); // T inferred to be String

Collection<Object> co = new ArrayList<Object>();


fromArrayToCollection(sa, co); // T inferred to be Object

Integer[] ia = new Integer[100];


fromArrayToCollection(ia, cs); // String NON è supertipo di
Integer

154
Iterazioni
• Definendo un nuovo tipo come collezione di oggetti si
vorrebbe disporre anche di un’operazione che consenta
iterazioni

• Se la collezione implementa List, è facile:


– Organizzazione lineare degli elementi
– Posso accedere mediante un indice

• Con altre collezioni?


– Il principio dell’ Information Hiding non consente
(giustamente!) l’accesso diretto all’organizzazione interna
di un contenitore

155
Iterare su collezioni
• Il for generalizzato non basta
– Fornisce una sola politica di scansione
– Come fare a prevedere in anticipo tutti gli usi?

• Soluzione inefficiente: avere un metodo che


restituisce un array con tutti gli elementi
• Soluzione inefficiente: definire size e get
for (int i = 0; i < set.size(); i++)
System.out.println(set.get(i));

– Su molte strutture dati l’accesso casuale


all’i-esimo elemento può essere inefficiente!
156
Soluzione generale
• Introdurre oggetti detti iteratori in grado di
“spostarsi” sugli elementi dei contenitori
– L’iteratore rappresenta un'astrazione del concetto
di “puntatore a un elemento del contenitore” e
permette di scorrere il contenitore senza bisogno
di conoscere l'effettivo tipo degli elementi
– Esempio con IntSet: oggetto “iteratore su IntSet”
con metodi: next(), per restituire il prossimo
elemento della collezione, e hasNext() per
verificare se siamo sull’ultimo elemento

157
Interface Iterator
• In java.util

public interface Iterator<E> {


public boolean hasNext();
public E next() throws NoSuchElementException;
public void remove(); // OPZIONALE
}

158
da Thinking in Java
• There’s not much you can do with the Java
Iterator except
– Ask a collection to hand you an Iterator using a
method called iterator()
– This Iterator will be ready to return the first element
in the sequence on your first call to its next() method
– Get the next object in the sequence with next()
– See if there are any more objects in the sequence with
hasNext()
– Remove the last element returned by the iterator with
remove()
159
Interfaccia Iterable
• Per standardizzare l’uso
degli iteratori, in Java esiste
anche l’interfaccia Iterable
(in java.lang)

• Le classi che implementano


l’interfaccia Iterable sono
tipicamente collezioni che
forniscono un metodo di
nome standard iterator()
per ottenere un oggetto
iteratore, con cui scandirne
gli elementi
160
Interfaccia Iterable
• È necessario implementare Iterable se si vuole
costruire una collezione da usare con un for
generalizzato
– Il prezzo della semplificazione lo paga
l’implementatore, che deve realizzare il metodo
iterator()
– ... in maniera efficiente, o nessuno lo userà!

161
Iteratore per IntSet: Esempio

public static boolean stampa(IntSet set){


for (Iterator<Integer> itr = set.elements(); itr.hasNext();)
System.out.println(itr.next());
}

public static boolean insiemePositivo(IntSet set){


for (Iterator<Integer> itr = set.elements(); itr.hasNext();)
if (itr.next()<0) return false;
return true;
}

162
Iterare su collezioni qualunque
• È un’astrazione sul controllo molto potente…

• …“the true power of the Iterator: the ability to separate the


operation of traversing a sequence from the underlying
structure of that sequence”
public class Iterations {
public static void printAll(Iterator<E> itr){
while(itr.hasNext())
System.out.println(itr.next());
// NB: conv. automatica a String
}

public static boolean cerca(Iterator<E> itr, <E> o){


while (itr.hasNext())
if (o.equals(itr.next())) return true;
return false;
}
} 163
Implementazione con classe interna
public class Poly implements Iterable{
private int[] trms; // coefficienti dei termini
private int deg; // il grado del polinomio

public Iterator<Integer> terms() {


return new PolyGen(this); }

// classe interna
private class PolyGen implements Iterator<Integer> {
// tutte le altre operazioni della classe
}
}

164
La classe per l’iteratore
import java.util.Iterator;
import java.util.NoSuchElementException;

// restituisce il grado (dei termini non nulli)


class PolyGen implements Iterator<Integer> {
private Poly p; // il Poly da iterare
private int n; // primo elemento non “consumato”

PolyGen(Poly lui) { p=lui; n = 0; }

public boolean hasNext(){


while(n <= p.deg) {
if(p.trms[n] != 0) return true;
else n++;
}
return false;
}

public Integer next() throws NoSuchElementException {


if(hasNext()) return p.trms[n++];
else throw new NoSuchElementException("Poly.terms");
}
}

165
Iteratori "stand alone"
• Un iteratore potrebbe non far parte di una classe,
ma essere una procedura statica “stand alone”

• Per esempio, un iteratore che genera tutti i


numeri di Fibonacci
– ... o un generatore di numeri primi

• Per questo motivo, serve definire la classe interna


come static: un metodo static di una classe non
può accedere a un costruttore di una classe
interna non static
166
Esempio
public class Nums {
public static Iterator<Integer> allFibos( ) {
return new FibosGen( );
}

// classe interna
private static class FibosGen implements Iterator<Integer> {
private int prev1, prev2; // i due ultimi generati
private int nextFib; // prossimo Fibonacci

FibosGen() {prev2=1; prev1=0;}

public boolean hasNext() {return true;}


public Integer next() {
nextFib = prev1+prev2;
prev2=prev1; prev1=nextFib;
return nextFib ;
}
}
}

167
Uso dell’iteratore per Fibonacci
public static void printFibos(int m) {
Iterator<Integer> g = Nums.allFibos();
while (g.hasNext()) { // sempre true…
int p = g.next();
if (p > m) return;
System.out.println(”Il prox Fibonacci e’ " + p);
}
}

168
Osservazioni
• “An iterator is an object whose job is to move through a sequence
of objects and select each object in that sequence without the
client programmer knowing or caring about the underlying
structure of that sequence; in addition, an iterator is usually what’s
called a “light-weight” object… one that’s cheap to create”

• Una collezione può avere più tipi di iteratori


– E quindi più metodi per generarli

• Esempio: una collezione potrebbe avere:

public Iterator<T> smallToBig()


public Iterator<T> bigToSmall()
public Iterator<T> oneEveryTwo()

169
Metodo remove
• Remove è un’operazione “opzionale”: se non è
implementata, quando invocata viene lanciata
UnsupportedOperationException

• In effetti, raramente è utile modificare una


collezione durante un’iterazione: più spesso si
modifica alla fine
– Esempio: trovo elemento e lo elimino, poi
iteratore non più usato

170
Metodo remove
• Semantica di remove() assicurata dal “contratto”
dell’interfaccia:
– Elimina dalla collezione l’ultimo elemento restituito da
next(), sempre che:
• Venga chiamata una sola volta per ogni chiamata di next()
• La collezione non venga modificata in qualsiasi altro modo (diverso
dalla remove) durante l’iterazione
– Altrimenti
• Viene lanciata un’eccezione IllegalStateException se il metodo
next() non è mai chiamato o se remove() già invocata dopo ultima
chiamata di next()
• Semantica della remove() non specificata se la collezione
modificata in altro modo (diverso dalla remove) durante
l’iterazione

171
INPUT/OUTPUT

172
I/O formattato
• Input
– Dobbiamo costruire uno scanner collegato allo “standard input
stream” System.in, poi usare i metodi della classe Scanner
• nextLine() legge la prossima riga
• next() legge la prossima stringa fino a uno spazio
• nextInt() legge il prossimo int
• nextDouble() legge il prossimo double
• hasNext() dice se c’è una stringa in input
• hasNextInt(), nextFloat(), nextBoolean()

• Output
– System.out fornisce anche il “buon vecchio” printf di C, con le
solite convenzioni di formattazione, più print e println

173
Esempio
public static void main(String args[]) {
Scanner in = new Scanner(System.in);
System.out.println("Come ti chiami?");
String nome = in.nextLine();
System.out.println("Quanti anni hai?");
int eta = in.nextInt();
System.out.println("Ciao "+nome+" tra un anno avrai "
+(eta+1)+" anni");
}

174
ANNOTAZIONI

175
Annotazioni
• @Deprecated indica un elemento deprecato e da non
continuare ad usare

• @Override informa il compilatore che l’elemento


sovrascrive un elemento dichiarato in una super classe
– Aiuta ad evitare errori

• @SuppressWarnings chiede al compilatore di non


generare warnings per quel metodo
– @SuppressWarnings({"unchecked", "deprecation"})

176