Oggetti e Classi in Java - Dipartimento di Ingegneria dell`Informazione

Transcript

Oggetti e Classi in Java - Dipartimento di Ingegneria dell`Informazione
M.P.
PARTE I
Corso
Metodologie di Programmazione
( Ling.di Prog.:Metod.di Progr.)
Oggetti e Classi
in Java
a.a.2002-2003
Docente : prof.B: Venneri
Lucidi delle lezioni relative al Modulo II :
Programmazione orient ata agli oggetti:
Java
1
2
Oggetti
Un oggetto rappresenta un dato, ed è costituito
da
stato: collezione di variabili (attributi)
comportamento: collezione di operazioni
(metodi)
Ogni oggetto ha
un’interfaccia
e ogni oggetto
ha un tipo
Esempio - contatore
STATO
int c;
METODI
void iniz(int i)
void incr()
void decr()
int val()
3
inizializza il contatore a i
incrementa il contatore di 1
decrementa il contatore di 1
restituisce il valore del
contatore
4
1
NOTA
Contatore
Invio di Messaggi
Variabile locale
Passaggio by value
void iniz( int i)
void incr( )
void decr( )
int val( )
int c;
v oid iniz( int i) { c= 1; }
v oid incr( ) { + + c; }
v oid decr( ) { - - c; }
int v al( ) { ret urn c; }
invio di messaggio = invocazione di un metodo
I n Java, come in altri linguaggi, si usa la notazione
con il punto: oggetto.metodo(…)(anche per
selezionare il campo attributo)
cont1
3
I NTERFACCI A
I MPLEMENTAZI ONE
cont2
10
CONTATORE
int x,y;
cont1.incr();
cont2.decr();
x = cont1.val();
y = cont2.val();
x ha valore 4 e y ha valore 9
5
THIS
Forma generale di una Classe
class nome-classe {
*tipo v1;
*tipo v2;
………
*tipo vn;
In un linguaggio tradizionale, per eseguire
una operazione su un dato, dovremmo
passare il dato come parametro:
incr(cont1) o iniz(cont1, 3)
Nella programmazione ad oggetti il dato è
l’oggetto implicito destinatario del messaggio
( this = parametro implicito)
6
cont1.incr() o cont1.iniz(3)
*tipo nomemetodo1(elenco-parametri)
{corpo del metodo}
………
*tipo nomemetodoN(elenco-parametri)
{corpo del metodo}
}
dove * è lo specificatore di accesso del membro
seguente
7
8
2
Esempio: contatore
Esempio :contatore
La classe definisce un tipo
Istanza di una classe = oggetto
In Java:
class Contatore {
int c;
void iniz(int i) {c = i;}
void incr() {++c;}
void decr() {--c;}
Nota: this implicito
int val() {return c;}
}
In ++c, c sta
per this.c
I st anze
cont 1
cont 2
3
10
9
Oggetti come parametri
10
Information Hiding
Per realizzare information hiding si usano le parole
riservate
public - interfaccia
private - implementazione
di fronte ad ogni membro della classe
I l parametro formale di un metodo può essere un
oggetto
L’oggetto, parametro attuale, può essere modificato.
class Contatore {
int c;
void swap(Contatore altro)
{int temp=c; c=altro.c; altro.c=temp;}
……………………………
class Contatore {
private int c;
public void iniz(int i) {c = i;}
public void incr() {++c;}
public void decr() {--c;}
public int val() {return c;}
}
Siano c1 e c2 due contatori: c1.swap(c2)
L’effetto ottenuto è che c1 e c2 hanno relamente
scambiato il loro valore
11
12
3
Controllo dell’accesso
Accesso alle classi
Specificatori per i membri delle classi:
Una classe ha solo due livelli di accesso
possibili ( lo specificatore è premesso
alla parola class )
public è accessibile da qualunque parte del
programma (anche da codice di altri pacchetti)
private è visibile solo all’interno della stessa classe
protected è visibile a tutte le classi dello stesso
pacchetto e solo alle sottoclassi di pacchetti diversi
default (nessuna specifica esplicita di accesso) è
visibile a tutte le classi dello stesso pacchetto ma non
alle classi di altri pacchetti
public accessibile da qualunque codice
default accessibile solo da codice all’interno
dello stesso pacchetto
13
Creare un’istanza
14
Recupero della memoria
new Contatore()
Garbage collection (Scheme, Prolog, Java)
Il programmatore puo’ solo allocare dati
dinamici
Crea un nuovo oggetto di tipo Contatore e ne
restituisce la referenza ( handle)
Tutti gli oggetti sono allocati dinamicamente
(quando si esegue la new) e sono manipolati
attraverso una handle (puntatore/ referenza)
15
Una procedura di sistema, il garbage collector,
si preoccupa di recuperare tutte le aree di
memoria nello heap non più raggiungibili in
modo da poterle riutilizzare
16
4
Come si inizializza un oggetto
Si può chiamare esplicitamente un metodo di
inizializzazione:
Contatore cont1;
cont1 = new Contatore();
cont1.iniz(3);
Java (come C+ + ) fornisce la nozione di
costruttore, che consente di inizializzare
automaticamente un oggetto al momento
della creazione.
Costruttori
class Contatore {
int c;
Contatore(int i) {c = i;}
……
}
Cost rut t ore
Ha lo stesso nome della classe!
Non ha tipo di ritorno ( è implicito il tipo della classe
stessa)!
Contatore cont1 = new Contatore(3)
crea un contatore - lo inizializza a 3.
17
18
Overloading di metodi
Overloading di costruttori
Overloading: metodi diversi possono avere lo
stesso nome (nello stesso campo di visibilità)
I metodi "overloaded" si distinguono uno
dall’altro in base alla lista e ai tipi dei
parametri ( non in base al tipo di ritorno)
Il compilatore, quando incontra una chiamata
al metodo, distingue di quale versione si
tratta in base al matching dei parametri
attuali-formali
19
E’ opportuno avere diversi costruttori
“overloaded” in ogni classe
Esempio:
class Contatore {
int c;
Contatore() {c = 0;}
Contatore(int i) {c = i;}
……
}
20
5
Chiamata del costruttore
La parola chiave static
Contatore cont1 = new Contatore(3)
Contatore cont1 = new Contatore()
La parola static può essere usata per identificare un
membro di una classe che verrà utilizzato senza
riferimento a un’istanza della classe: svolge il ruolo di
una variabile globale, tutte le istanze di quella classe
condividono la stessa variabile di classe
Esempio:
I n una classe BankAccount, vogliamo fare in modo che
il costruttore assegni il numero di conto in modo
progressivo (1,2,…):tenere l’ultimo numero di conto
assegnato.
Nel primo caso viene chiamato il costruttore con
parametro intero, nel secondo il costruttore senza
parametri
Java crea un costruttore di default senza parametri per
le classi per le quali non è disponibile alcun
costruttore
quello di default non è più disponibile se è
definito un costruttore!
21
22
Esempio di variabile di classe
Metodi static
public class BankAccount {
……..
private int accountNumber;
private static int last=0
BankAccount ()
{// genera il numero di conto successivo
last++;
//aggiorna l’oggetto
accountNumber=last ;} ……
}
Per chiamare un metodo (così come una
variabile) static dall’esterno della sua classe,
si usa la solita notazione con punto preceduto
dal nome della classe
nomeclasse.metodo()
Questi metodi possono chiamare solo altri
metodi static, possono accedere solo a
variabili static,etc…, ossia non hanno nessun
riferimento a un this implicito.
Attenzione: le variabili statiche non sono, in
genere, desiderate.
23
24
6
Variabili final
Classi interne
Una variabile può essere dichiarata
“final” per impedire la modifica del so
contenuto (convenzione: lettere
maiuscole per i loro nomi, come per le
costanti).
Quando è necessaria una classe per uno scopo
molto tattico e limitato, si può dichiarare
questa inner-classe all’interno del metodo che
ne ha bisogno.
Spesso, in questi casi, si vuole solo usare un
esemplare di questa classe per gli scopi del
metodo: è possibile definire una classe
anonima.
il significato di final è del tutto
diverso per i metodi!
25
26
CLASSI ASTRATTE
La parola chiave abstract
Definizione Una classe astratta è una
classe che contiene almeno un metodo
non implementato
I n Java
ogni metodo astratto può essere evidenziato con il
prefisso abstract
la classe che ha (eredita) almeno un metodo astratto
(senza body) deve essere etichettata con abstract
(ma non viceversa)
Es: abstract class Figura
{
…….
public abstract void disegna();
……
}
Ruolo : serve come base di una
derivazione, fattorizza comportamenti
comuni a più classi, etc..,
non può essere istanziata
27
28
7
INTERFACCE
Interfaccia vs.Classe
Definizione Una INTERFACE è una classe
totalmente astratta : non contiene attributi e
tutti i metodi sono astratti
Ruolo : dichiara un insieme di metodi con le
loro signature, quindi definisce l’interfaccia
comune di diverse classi derivate (enfatizza
separazione fra interfaccia e
implementazione)
In Java: una classe concreta che deriva da
un’interfaccia implementa l’interfaccia
(derivazione particolare)
Tutti i metodi sono automaticamente abstract
Tutti i metodi sono automaticamente public
Non contiene attributi
Sintassi Java
public interface NomeInterfaccia
{
Elenco metodi con signature
}
Esercizio: interfaccia per Pile e Code
PARTE II
Ereditarietà
29
Derivazione, Ereditarietà e
Polimorfismo
30
class Finestra {
Rettangolo r; ....
void disegnaCornice()
{...}
void disegnaContenuto()
{...}
}
class FinestraConTitolo extends Finestra {
String titolo;
void disegnaTitolo() {...}
}
31
32
8
Sottoclassi
Extends (Java)
Classe = Modulo
FinestraConTitolo
estende Finestra
E‘ la parola chiave per definire una
gerarchia di classi
class ClassePadre {
[…]
}
-eredita attributi e
metodi
class ClasseFigliaUno extends ClassePadre {
[…]
}
class ClasseFigliaDue extends ClassePadre {
[…]
}
-aggiunge attributi e
metodi
-riscrive metodi
33
Sottotipo
34
Controllo statico dei tipi
Java, come molti altri linguaggi, effettua un
controllo dei tipi (type checking) statico.
Derivazione di classe=
Specializzazione del tipo
Statico: fatto dal compilatore prima di iniziare
l’esecuzione del programma.
Dinamico: fatto dall’interprete durante l’esecuzione
( a runtime)
cla ss B e x t e n ds A
A
B
x
B
x
A
35
Il type checking statico garantisce che non ci
saranno errori durante l’esecuzione.
36
9
Controllo statico dei tipi
Finestra f;
FinestraConTitolo ft;
...
ft.disegnaCornice();
f.disegnaCornice();
ft.disegnaTitolo();
f.disegnaTitolo();
Ereditarietà singola
Ogni sottoclasse ha
una sola
sopraclasse.
Struttura ad albero.
corretto
I n Java la classe
Object è la radice
della gerarchia.
errore di compilazione
f è una finestra e non ha il
metodo disegnaTitolo
Qualunque oggetto
è un Object.
Type checking statico: il compilatore controlla che
per una variabile si chiami un metodo definito
(ereditato) per la classe di quella variabile
37
Ereditarietà multipla
38
Polimorfismo
Articolo
codice
prezzo
ApparecchioElettrico
tensione
potenza
I tipi primitivi non
sono Object.
POLI MORFISMO: proprietà
di un oggetto di avere più
di un tipo, in accordo alla
relazione di ereditarietà.
Esempio:
D sottoclasse di B e di A
un oggetto D è un B ed è
un A
un oggetto di tipo (classe)
D è anche un oggetto di
tipo (classe) B e anche un
oggetto di tipo (classe) A
Televisore
pollici
In Java non è permessa.
Deve essere trasformata con relazione d’uso
39
40
10
Polimorfismo
A
d
b
a
a
a; B b; D d;
= new D();
= new D();
= new D();
= b;
Type Matching
L’assegnamento
x = espr
è corretta per il compilatore se:
quest i
assegnam ent i
sono t ut t i legali
perché un
ogget t o di t ipo D
ha anche t ipo B
e t ipo A
Analogamente se x è un parametro formale di
un metodo e espr è il parametro attuale
(della chiamata)
41
Upcasting
Controllo statico.
42
Upcasting
Upcasting: ci si muove da un tipo specifico ad uno
più generico (da un tipo ad un suo “sopratipo”).
Tipo di espr è uguale a tipo di x
Tipo di espr è sottotipo del tipo di x
L’upcasting è sicuro per il type checking: dato che
una sottoclasse eredita tutti i metodi delle sue
sopraclassi, ogni messaggio che può essere inviato
ad una sopraclasse può anche essere inviato alla
sottoclasse senza il rischio di errori durante
l'
esecuzione.
43
Poligono p;
Rettangolo r;
...
p.disegna();
r.disegna();
p.perimetro();
r.perimetro();
corretto per il
compilatore
44
11
Upcasting
Overriding
Poligono p;
Rettangolo r;
p = r;
r.diagonale(); corretto
errore di
compilazione
p è di tipo Poligono
e non ha il metodo
diagonale
45
Riscrivere un metodo
Spesso un metodo di una sottoclasse definito
per overriding non ridefinisce completamente
il metodo della sua sopraclasse, ma lo
estende.
Ad esempio, il metodo disegnaCornice della
FinestraConTitolo estende il metodo
disegnaCornice della Finestra, con del codice
specifico per disegnare il titolo.
Si deve MANTENERE LA STESSA SIGNATURA
class Finestra {
Rettangolo r; ....
void disegnaCornice() {...}
void disegnaContenuto() {...}
}
p.diagonale();
se si facesse il controllo a runtime,
sarebbe corretto perché p è legata ad un
rettangolo (che possiede il metodo
diagonale).
Una sottoclasse può anche riscrivere (OVERRI DI NG)
un metodo della sopraclasse.
Per evitare di duplicare il codice, si può far
riferimento ad un metodo della sopraclasse
con lo stesso nome mediante la notazione 47
super.
class FinestraConTitolo extends Finestra {
String titolo;
void disegnaCornice() { … disegna la cornice
con il titolo …}
}
46
super
class FinestraConTitolo extends Finestra {
String titolo;
void disegnaCornice() {
super.disegnaCornice();
... nuovo codice ...
}
}
super.disegnaCornice() chiam a il m et odo
disegnaCornice della sopraclasse ( che alt rim ent i non
sarebbe visibile, causa lookup dei m et odi dal basso)
Not a Senza super si avrebbe un loop infinit o!
48
12
Cella
Costruttori in classi derivate
class Cella {
int contenuto=0;
int get() {return contenuto;}
void set(int n) {contenuto=n;}
}
class CellaConMemoria extends Cella {
int backup=0;
void set(int n) {backup=contenuto;
super.set(n);}
void restore() {contenuto=backup;}
}
Il costruttore della classe derivata deve
richiamare il costruttore della classe base per
inizializzare i campi ereditati
Java inserisce automaticamente
una chiamata al costruttore della classe base
nel costruttore della classe derivata
(prima azione)
49
Polimorfismo:
perdita d’ informazione
Super come costruttore
Super è utilizzata
per chiamare un
costruttore della
sopraclasse
La chiamata di
this(… ) o
super(…) deve
essere la prima
istruzione del
costruttore!
class ClasseSopra {
public ClasseSopra(int x) {
System.out.println("Costruttore ClasseSopra: " + x);
}
public ClasseSopra() {
this(-1);
System.out.println("Costruttore ClasseSopra");
}
}
class ClasseSotto extends ClasseSopra {
public ClasseSotto (int x) {
super(x);
System.out.println("Costruttore ClasseSotto");
}
public ClasseSotto() {
super();
super();
System.out.println("ostruttore ClasseSotto");
}
}
50
51
Poligono p;
Rettangolo r;
...
p = r;
p.disegna();
p.perimetro();
corretto per il compilatore,
ma
quale metodo si esegue?
Quello di Poligono o
quello di Rettangolo?
52
13
Binding dinamico
Binding dinamico
Un oggetto decide quale metodo applicare a se stesso
in base al proprio tipo
BI NDI NG DI NAMICO: la forma di un oggetto determina
dinamicamente quale versione di un metodo
applicare
Polimorfismo + Binding dinamico
Oggetti diversi, ma con una interfaccia comune, sono
trattati in modo omogeneo, ma ciascuno può
rispondere a una chiamata di metodo in modo
specifico
p.perimetro();
Si esegue il metodo perimetro dell’oggetto a cui p fa riferimento in
quel momento.
Poligono p = new Poligono();
si esegue il metodo
Rettangolo r = new Rettangolo();
perimetro di Poligono
p.perimetro();
p = r;
si esegue il metodo
perimetro di Rettangolo
p.perimetro();
53
54
Uso del binding dinamico
Lookup dei metodi
class Finestra {
Rettangolo r; ....
void disegnaCornice() {...}
void disegnaContenuto() {...}
void rinfresca() {
disegnaCornice();
disegnaContenuto();
}
}
Ricerca di un metodo per un oggetto:
la sottoclasse verifica se possiede o meno un
metodo con tale nome e con gli stessi parametri
(stessa signatura), se si‘ , lo usa
se no, si cerca nella classe padre… (e cosi` via
nella gerarchia)
Un metodo definito in una sottoclasse e avente la
stessa signatura di un metodo di una delle classi
antenate nasconde il metodo di quest’ultima alla
sottoclasse
class FinestraConTitolo extends Finestra {
String titolo;
void disegnaCornice() { … disegna la cornice
con il titolo …}
}
55
56
14
Overloading vs. overriding
class ClasseA {
public void metodo (int x) {
System.out.println("Metodo di Classe A: " + x);
}
OVERLOADING
public void metodo() {
System.out.println("Metodo di Classe A");
this.metodo(-1);
}
} OVERRIDING
OVERLOADING
class ClasseB extends ClasseA {
public void metodo (int x) {
System.out.println("Metodo di Classe B esteso");
super.metodo(x);
}
OVERLOADING
public void metodo() {
System.out.println("Metodo di Classe B esteso");
super.metodo();
}
}
Overriding di metodi privati
Overloading:
stesso
nome ma
diversa lista
dei tipi di
parametri
Overriding:
stessa firma
ma classi
diverse
(purché
nella stessa
gerarchia)
class ClasseUno {
public void metodo1 () {
System.out.println("Eseguito
metodo 1 nella classe Uno");
this.metodo2();
}
private void metodo2() {
System.out.println("Eseguito
metodo 2 nella classe Uno");
}
class ClasseDue extends ClasseUno {
public void metodo2 () {
System.out.println("Eseguito
metodo 2 nella classe Due");
}
}
La parola chiave final
Se invio il messaggio
metodo1 ad un oggetto
di tipo ClasseDue quale
metodo metodo2 viene
eseguito?
Binding dinamico?
Dichiarare che un
metodo è privato
equivale a “cambiargli
nome”
A parte ClasseUno
nessuno sa o deve
sapere dell’esistenza di
metodo2 di ClasseUno
58
Ereditarietà e Accesso
}
57
Anche se la sottoclasse comprende tutti i
membri della sopraclasse, non può accedere
ai membri dichiarati private nella sopraclasse
Un membro di una classe dichiarato private
rimane tale anche per le sottoclassi
Java non consente di riscrivere un metodo
per restringerne l’accesso (es. errore se
public nella superclasse e privat e nella
sottoclasse)
59
Come modificatore all’inizio della
dichiarazione di un metodo: il metodo non
può essere riscritto nelle sottoclassi
(risoluzione con binding statico)
Come modificatore davanti alla dichiarazione
di una classe : la classe non può essere
ereditata (implicitamente tutti i suoi metodi
sono final)
Una classe non può essere contemporaneamente
abstract e final !
60
15
Derivazione e classi astratte
Derivazione e Interfacce
Da una classe astratta possiamo derivare
altre classi astratte e/ o classi concrete
(valgono tutte le regole della normale
derivazione di classi)
Un’ interfaccia può ereditare da altre
interfacce utilizzando la parola chiave
extends
Interface Prima {
void m1();
void m2();
}
Interface Seconda extends Prima{
void m3();
}
Uso: delle classi astratte posso dichiarare
referenze a oggetti, anche se non posso
creare oggetti
uso del polimorfismo
L’interfaccia Seconda include la Prima
61
Implementazione di Interfacce
62
Una classe può implementare un’I nterfaccia
con la parola chiave implements
Interfacce vs. classi astratte
Le classi astratte possono essere miste, ossia
possono contenere anche metodi non astratti.
class MyClass implements Seconda
{deve implementare m1, m2 e m3
}
Ereditarietà singola per le classi, anche
astratte, ma una classe può implementare più
interfacce
1. MyClass deve implementare anche i metodi
ereditati!
2. Polimorfismo:
Seconda obj = new MyClass();
obj.m2();
3. I metodi che implementano i metodi dell’interfaccia
• devono essere dichiarati “public”
• devono avere la stessa signatura dell’interfaccia
63
Una classe può implementare più di una
interfaccia e contemporaneamente estendere
una classe ( parziale ereditarietà multipla,
matrimonio di convenienza)
64
16
Interfacce multiple
interface A {
void metodoA();
}
Verso l’ereditarietà multipla
interface B {
void metodoB();
}
Una classe può implementare più
interfacce senza i problemi standard
della multipla ereditarietà:
non ci sono attributi (quindi non c’è il
problema della loro duplicazione).
non ci possono essere problemi di name
clash di metodi, perché i metodi
ereditati sono astratti.
class C implements A,B {
public void metodoA() {System.out.println("sono A");}
public void metodoB() {System.out.println("sono B");}
public static void main(String[] args) {
A a = new C();//l’oggetto è visto come un A
B b = new C();//l’oggetto è visto come un B
a.metodoA();
b.metodoB();
a.metodoB(); //errore: A non ha il metodoB
}
}
65
66
Horror Show (un esempio)
Interface Mostro {
void minaccia();
}
Interface MostroPericoloso extends Mostro
{
void distrugge();
}
Interface Letale {
void uccide();
}
Interface Vampiro extends
MostroPericoloso,Letale
{
void bevesangue(Mostro m);
}
67
Horror Show (continua)
Class Dragone implements MostroPericoloso
{
public void minaccia() {…};
public void distrugge() {…};
}
Class Dracula implements Vampiro {
public void minaccia() {…};
public void distrugge() {…};
public void uccide() {…};
public void bevesangue(Mostro m) {…};
}
68
17
Horror Show (continua)
Interfacce per viste diverse
Class HorrorShow {
static void azione1(Mostro m)
{m.minaccia()};
static void azione2(MostroPericoloso mp)
{mp.minaccia(); mp.distrugge};
public static void main (…)
{ Dragone animale = new Dragone();
Vampiro vp = new Dracula();
azione1(animale);
azione2(animale);
vp.bevesangue(animale)
}
Interface CanSwim
{void nuota();}
Interface CanFly
{void vola();}
Class Azione
{public void combatte();}
Class Eroe extends Azione
implements CanSwim, CanFly
……………………
Un Eroe è visto come CanSwim o come CanFly, a
seconda che sia riferito da una variabile di tipo
CanSwim o di tipo CanFly
(“è visto” = insieme dei servizi offerti)
69
70
Betti
BettiVenneri:
Venneri:
13-11-02
13-11-02
Un esempio
Un esempio (continua)
Un oggetto DataSet gestisce dei valori in ingresso, aggiungendo,
tenendo il massimo, calcolando la media etc…
public class DataSet
{// Costruisce un insieme di dati vuoto.
// Aggiunge un valore all’insieme dei dati.
public void add(double x)
{
sum = sum + x;
if (count == 0 || maximum < x) maximum = x;
count++;
}
……
//restituisce il massimo
public double getMaximum()
{
return maximum;
}
public DataSet()
{
sum = 0;
count = 0;
maximum = 0;
private double sum;
private double maximum;
private int count;
}
}
71
72
18
Sviluppare codice riutilizzabile
-La classe DataSet potrebbe essere utilizzata anche per
ottenere il minimo , il massimo, etc.. su conti bancari
.
Supponiamo di avere una classe BankAccount con un campo
“Saldo” e un metodo “getSaldo” che restituisce questo valore
Usare Interfacce per migliorare il riuso
E per analizzare le “monete”?
Soluzione: invece di avere tante classi DataSet,
utilizziamo un’interfaccia comune “Misurabile” tale
che monete, BankAccount, etc…siano tutti
“Misurabili”
public class DataSet
Bisogna modificare la classa DataSet solo per cambiare il tipo dei
dati:
{//
Aggiunge un valore all’insieme dei dati.
public void add(Misurabile x)
{
sum = sum + x.getMisura;
if (count == 0 || maximum.getMisura < x.getMisura)
maximum = x;
count++;
}
public void add(BankAccount x)
public BankAccount getMaximum()
private BankAccount maximum;
Ma il resto del codice resta uguale!
73
Usare Interfacce…(continua)
74
Usare Interfacce…(continua)
…..
public interface Misurabile
{double getMisura()
}
Public class BankAccount implements Misurabile
{……
public double getMisura
{ return saldo; }
…}
//Restituisce il massimo
public Misurabile getMaximum()
{
return maximum;
}
private double sum;
private Misurabile maximum;
private int count;
}
}
75
Così anche le classi “moneta” etc devono implementare
Misurabile, ossia fornire la propria implementazione di
getMisura
76
19
Aggiungere Interfacce strategiche
Usare Interfacce…(continua)
Il disaccoppiamento fra
BankAccount,Moneta,etc. e DataSet rende
riutilizzabile DataSet
DataSet
BankAccount
Problema :
Posso modificare le classi BankAccount,… solo perché
sono definite da me
“interface”
Misurabile
Posso “misurare” un oggetto in un unico modo
Soluzione: la responsabilità della misurazione non deve
ricadere sugli oggetti, è meglio che un altro oggetto
effettui le misurazioni.
Ci sarà un oggetto-misuratore per ogni tipo di dati,
questi misuratori implementano un’interfaccia
comune Measurer.
Moneta
77
78
Aggiungere Interfacce…(continua)
Aggiungere Interfacce…(continua)
File Measurer.java
/**
Descrive una qualsiasi classe i cui oggetti
possano
misurare altri oggetti.
*/
public interface Measurer
{
/**
Calcola la misura di un oggetto.
anObject è l’oggetto da misurare
return la misura
*/
double measure(Object anObject);
}
La classe DataSet è ora costruita con un
oggetto m di tipo Measurer, a cui sono
demandate le operazioni di misurazione
Per ottenere un misuratore dei BankAccount
ci sarà la classe opportuna
Class BankMeasurer implements Measurer
che implementa il suo metodo measure
(attenzione: il parametro è object,quindi sarà
necessario un downcast esplicito!)
79
80
20
File DataSet.java
public class DataSet
{
/*Costruisce un insieme vuoto di dati con un misuratore
assegnato. aMeasurer è il misuratore che viene usato
per misurare i valori dei dati
*/
public DataSet(Measurer aMeasurer)
{
sum = 0;
count = 0;
maximum = null;
measurer = aMeasurer;
}
/**
Aggiorna il massimo e aggiunge il dato
*/
public void add(Object x)
{
sum = sum + measurer.measure(x);
if (count == 0
|| measurer.measure(maximum) <
measurer.measure(x))
maximum = x;
count++;
}
…………….
81
82
Polimorfismo Parametrico
Tipi polimorfi =
Tipi con un parametro di tipo
Esempio: Pila [ t]
Il comportamento della Pila può
essere definito per un generico
tipo t degli elementi
/**
Restituisce il dato maggiore tra i dati
inseriti.
*/
public Object getMaximum()
{
return maximum;
}
private
private
private
private
(ortogonale al polimorfismo per sottotipo)
double sum;
Object maximum;
int count;
Measurer measurer;
}
83
84
21
Classi generiche
Classi Parametriche in C+ +
La proprietà che tutte le
classi sono discendenti
di Object consente di
definire strutture dati
che possono contenere class Pila {
oggetti di qualunque
...
tipo.
void push(Object x)
Template <T>
Class Pila
……
Void push (T elemento)….
Per utilizzare una pila di BankAccount:
Pila <BankAccount> piladiconti;
Piladiconti.push(contoRossi);
{...};
Object pop() {...};
Non si può specificare il }
tipo degli elementi della
pila: possono essere
oggetti qualunque.
PROBLEMA
Gli oggetti della Pila sono solo Object!
85
86
Downcasting in Java
class Pila {
...
void push(Object x) {...};
Object pop() {...};
}
Downcasting
Downcasting: ci si muove da un tipo più
generale ad uno più specifico (da un tipo ad
un sottotipo)
Supponiamo di sapere che
sulla pila vengono messi solo
rettangoli. Come possiamo
utilizzare gli oggetti estratti
dalla pila?
Se A è un sottotipo di B e se espr ha tipo B,
( A) espr ha tipo A
Pila s; Rettangolo r;
...
s.push(new Rettangolo());
r = s.pop();
Java non supporta la programmazione
Generica (si usa Object ma è richiesto un
cast)
C+ + mette a disposizione i Template:
L’assegnamento
Errore di compilazione.
pop restituisce un Object, che è
più generale di Rettangolo
87
A a = (A) espr è corretto per il compilatore
(A) espr può dare errore a run time, se
l’oggetto ottenuto valutando espr non ha tipo
A.
88
22
Downcasting
PARTE III
class Pila {
...
void push(Object x) {...};
Object pop() {...};
}
...
Pila s; Rettangolo r;
...
s.push(new Rettangolo());
r = (Rettangolo) s.pop();
Argomenti di
approfondimento
Accettato dal compilatore.
Può dare errore a run time.
Controllo a run-time.
Quando si esegue questa
istruzione
si controlla che l'
oggetto
restituito
da pop sia veramente un
rettangolo.
89
La classe di tutte le classi
90
La classe Object
In Java ciascuna classe che non estende altre
classi estende automaticamente la classe
Object : Object è la radice di ogni gerarchia
di derivazione.
La classe Obj ect si trova nel pacchetto
java.lang, che viene automaticamente
imporatato in ogni programma
Dunque, ogni oggetto è anche un Object
91
L’utilità della classe Object consiste non solo nella
possibilità di usare referenze Object per oggetti di
qualunque classe ma anche nella possibilità di
usare/riscrivere alcuni metodi, che in Object sono
presenti ma molto generici.
Alcuni metodi utili: toString, equals, clone
1) toString : restituisce una stringa che descrive
l’oggetto su cui è invocato ( di fatto è invocato ogni
volta che concatenate “+ ” una stringa con un
oggetto)
Es: BankAccount marioRossi =
new BankAccount();
String s= marioRossi.toString();
92
23
getClass ( + getName )
toString
s non è molto utile , si riferisce a qualcosa come
BankAccountd1229bf
ossia nome della classe e un indirizzo di memoria
Esempio di Overriding del metodo:
Public class BankAccount implements Misurabile
{……
public String toString()
{return “BankAccount[saldo=“+ saldo +”]” ;
}
…}
Adesso con
String s = marioRossi.toString();
s si riferisce alla stringa “BankAccount[saldo=3000]”
UTILE quando volete controllare l’esecuzione del
programma
final Class getClass: ottiene un oggetto di tipo Class
usiamo anche il metodo getName che, invocato su un
oggetto di tipo Class, restituisce il nome della classe
Esempio:
Public class BankAccount
{…… public String toString()
{return getClass().getName()+
“[saldo=“+ saldo +”]” ;
…}
}
93
Per Esercitazione
Il metodo equals
2) Equals:
Boolean equals (Object obj)
Esercizio per prossima esercitazione:
sia AccountUno una sottoclasse di BankAccount
AccountUno mio=…..;
System.out.println (mio);
94
Restituisce true se l’oggetto su cui si invoca è
uguale a obj, nel senso che contiene la
stessa informazione.
Per controllare lo stato corrente dell’oggetto, facciamo la
stampa….
stampiamo il nome della sottoclasse o quello della sopraclasse?
Se vogliamo stampare oltre al nome della sottoclasse anche un
nuovo attributo introdotto in AccountUno?
95
Si può riscrivere, ma dobbiamo di nuovo usare
un cast!
96
24
Il metodo clone
Esempio di clonazione
Clonare = fare una copia di un oggetto
3) metodo Clone ( ) della classe Object
Protected
Quindi deve essere forzato a public nella classe
che si vuole rendere clonabile (es.
public Object clone() = return super.clone() )
Restituisce un tipo Object
Dunque può richiedere downcast sul risultato
La classe clonabile deve implementare l’interfaccia
“Cloneable” : se chiamo obj.clone() e la classe di obj
non implementa Cloneable , è sollevata un’eccezione
NOTA : Bisogna gestire l’eccezione associata
Esempio
class Nuova implements Cloneable
{ public Object Clone()
{ Object ob = null;
try { ob = super.clone;}
catch (CloneNotSupportedException e)
{ return null oppure e.stampa qualcosa;}
return ob;
}
}
Clone esegue ( definisce lo spazio necessario per
eseguire) una copia bit a bit dell’oggetto
97
Clonazione e Derivazione
98
Copia Profonda
Ora tutte le classi derivate da Nuova sono
automaticamente clonabili (a meno che
non sia disattivata la clonazione)
Attenzione: devo ridefinire clone se non
voglio che venga usato quello della
superclasse!
99
La Clone esegue una copia superficiale: ad
es., se alcuni campi contengono referenze ad
altri oggetti, questi risultano condivisi
dall’oggetto clonato e dal suo clone
Programmare l’operazione di copia profonda:
ma non posso sempre sapere se le classi
degli oggetti interni sono Clonabili (in caso
negativo, è sollevata l’eccezione)
(Alternativa: ottenere la copia profonda
usando la serializzazione dgli oggetti)
100
25
SERIALIZZAZIONE
Esempio di serializzazione
Problema: persistenza degli oggetti fra un’esecuzione e
l’altra
Java offre una semplice soluzione fornendo il meccanismo
Serializzazione: un qualunque oggetto di una classe,
che implementi l’interfaccia Serializable, può essere
convertito in una sequenza di byte e ripristinato (su / da
file: si usano flussi di oggetti invece che flussi binari
“import java.io”
metodo w riteObject (della classe
ObjectOutputStream)
metodo readObject (della classe
ObjectI nputStream) che, però, restituisce tipo Object
class BankAccount implements Serializable {………….}
……..
ObjectOutputStream out = new ObjectOutputStream ( new
FileOutputStream (“dato”) );
out.writeObject (mioconto);
out.close;
ObjectInputStream in = new ObjectInputStream ( new
FileInputStream (“dato”) );
BankAccount mioconto = (BankAccount) in.readObject()
in.close;
Se si esegue la serializzazione in un unico flusso, la rete degli
oggetti è salvata senza nessuna modifica: mettete i vostri
dati in un contenitore e, alla fine, salvate questo
contenitore !
101
102
RTTI
La Riflessione
I n fase di esecuzione posso avere
informazioni sul tipo dell’oggetto
Possibilità di ottenere informazioni
sulle classi in fase di esecuzione
Ad es:
Class c = x.getClass( )
c è un oggetto Class (la classe di x)
c. getName
restituisce il nome della classe
oggettoclasse isI stance ( nomeoggetto)
restituisce true se l’oggetto è istanza della classe
(il file .class viene aperto ed esaminato in esecuzione)
La classe Class supporta la Riflessione
Java.lang.reflect mette a disposizione alcuni
metodi come getFields( ) , getMethods( ) ,….
per ottenere array degli oggetti che rappresentano i
campi, i metodi, …..di una classe
(esempi di uso della Riflessione nel cap.12 del testo)
103
104
26
Documentazione del codice
APPENDICE
I l programma java.doc genera automaticamente un insieme di pagine
HTML che descrivono (documentano) le classi.
Per ogni metodo:
commento di documentazione: incluso fra / * * e * /
@param nome del parametro e spiegazione
@return descrizione del valore restituito
Per ogni classe:
Commento di documentazione
USO: il comando
javadoc nomeclasse.java
(anche prima della compilazione)
produce il file “ nomeclasse.html “ che descrive l’interfaccia della classe
(e che si può esaminare con un browser)
O.O.P.
Programmazione/ Progettazione per Contratto
Una classe descrive un’astrazione uniforme sulle
proprietà di un insieme di oggetti; queste proprietà
sono l’insieme degli stati in cui gli oggetti possono
trovarsi e il loro comportamento
I nvariante di classe: asserzione che gli oggetti
della classe soddisfano in ogni momento (dopo la loro
costruzione, dopo la chiamata di un modificatore):
VI NCOLI DI CONSI STENZA
105
106
Invarianti di Classi e…
Asserzioni dei metodi
Precondizioni e Postcondizioni per ogni metodo:
Precondizione: responsabilità per chi chiama il metodo
Postcondizione: responsabilità per chi ha progettato il metodo
Derivazione di B da A: per ogni metodo m di A riscritto in B: i) la
sua precondizione in B non è più restrittiva di quella in A (può
essere più “larga”) ii) viceversa per la postcondizione
covarianza- controvarianza
Es: invariante di stack
numElementi> = 0 & numElementi< = dimensionestack
Derivazionedi B da A:
B eredita l’invariante e aggiunge specifiche
I nv.B
I nv.A
SpaziostatiB è incluso in SpaziostatiA
ComportamentoB estende ComportamentoA
Correttezza di una Classe
Una classe è corretta rispetto alle sue asserzioni sse:
1.
Per ogni metodo m
{ I NV & prec-di-m} bodym { I NV & postc-di-m}
2.
Dato il costruttore C
{ prec-di-C} bodyC { I NV}
107
108
27
Covarianza-Controvarianza
Principio della sostitutività:
Ogni oggetto/ codice di tipo più piccolo può essere usato
dove ne è richiesto uno di (ha anche) tipo più grande
Regola di sottotipo per le funzioni:
Sia f: A →B: A’→B’ ⊆ A →B se e solo se A ⊆A’ e B’ ⊆B.
Dunque la regola corretta per permettere di cambiare la
signatura di f nella sua riscrittura nella sottoclasse è
quella di riscriverla con un tipo più piccolo A’→B’ , ossia
Controvarianza dell’argomento : il tipo dei parametri
cresce
Covarianza del risultato : il tipo del risultato decresce
109
28