Esercitazione del 7 marzo 2008
Ereditarieta’
Esercizio: soluzione
• Implementare la seguente specifica che definisce
un tipo di dato Libro
public class Libro{
//OVERVIEW: un Libro memorizza il titolo (String),
//l’autore (String), numero di copie (int)
//la rappresentazione
private String titolo;
private String autore;
private int copie;
public Libro(String t,String a,int n){
//REQUIRES: n >0
//EFFECTS: crea un nuovo Libro con titolo t,
//autore a, e numero di copie n
titolo=t; autore=a; copie =n;}
public void somma(int num) {
//REQUIRES: num>0
//MODIFIES:this
//EFFECTS: aggiorna il numero delle copie sommando
//num
copie =copie + num;}
}
public String autore(){
//EFFECTS: restituisce l’autore di this
return autore;}
public String titolo(){
//EFFECTS: restituisce il titolo di this
return titolo;}
public int copie(){
//EFFECTS: restituisce il numero di copie di this
return copie;}
public boolean equals(Libro p){
//EFFECTS: restituisce true se this e p
// sono uguali, altrimenti false
if (autore.equals(p.autore) &&
titolo.equals(p.titolo) &&
copie.equals(p.copie))return true
else return false;}
}
Esempio
Libro l1=new Libro(“DC”,”Dante”,1);
Libro l2=new Libro(“DC”,”Dante”,1);
l1==l2=======> false
l1.equals(l2) =====>true
public class ProcLibro{
//OVERVIEW: definisce metodi statici per
//effettuare operazioni su un array di Libri
public static boolean cerca(Libro[] b, Libro p){
//EFFECTS: restituisce true se p occorre in b,
//altrimenti false
}
public static String cerca-titolo(Libro[] b, String a){
//EFFECTS: restituisce il titolo di un Libro
//appartenente a b con autore a, se non ce ne
// sono la stringa vuota}
public static int all-copie
(Libro[] b, String a, String t){
//EFFECTS: restituisce il numero di copiein b del
//Libro che ha autore a e titolo t }
}
public class ProcLibro{
//OVERVIEW: definisce metodi statici per
//effettuare operazioni su un array di Libri
public static boolean cerca(Libro[] b, Libro p){
//EFFECTS: restituisce true se p occorre in b,
//altrimenti false
for (int i=0; i <b.lentgh,i++)
{if (b[i].equals(p))return true;
}
return false;
}
public static String cerca-titolo(Libro[] b,
String a){
//EFFECTS: restituisce il titolo di un Libro
//appartenente a b con autore a, se non ce ne
// sono la stringa vuota
String result=“”;
for (int i=0; i <b.lentgh,i++)
{if (b[i].autore().equals(a))
result=b[i].titolo();}
return result;
}
public static int all-copie(Libro[] b, String a, String t)
//EFFECTS: restituisce il numero di copie in b del
//Libro che ha autore a e titolo t
int numero=0;
for (int i=0; i< a.length; i++)
{if (b[i].titolo().equals(t) && b[i].autore().equals(a))
numero=numero+ b[i].copie();
}
return numero;
}
}
•NOTA: i metodi della classe ProcLibro sono indipendenti
dalla implementazione di Libro
•Usiamo Libro in base alla sua interfaccia pubblica
Esercitazione di oggi
•
•
•
•
Definire sottoclassi (ereditarieta’)
Overriding
Specificatori di accesso (private, protected)
Principio di Sostituzione
Ereditarieta’
Se c1 è una sottoclasse di (estende) c2
• le variabili e metodi statici di c2 (e delle sue
superclassi) sono visibili direttamente da c1
•variabili e metodi di istanza di c2 (e delle sue
superclassi) diventano anche variabili e metodi di
istanza di c1 (a meno di overriding)
Overriding
Una sottoclasse puo’ sovrascrivere un metodo della
superclasse (stesso nome, stessi parametri, stesso tipo)
In tal caso sugli oggetti della sottoclasse viene
utilizzato il metodo sovrascritto (quello piu’ specifico)
Costruttori
Anche per i costruttori esiste un meccanismo di ereditarietà
• se c1 e’ sottoclasse di c2
•all’atto della creazione di una istanza di c1 si esegue
automaticamente il costruttore (senza parametri) di c2
(per inizializzare le variabili ereditate)
Esercizio 1
• Sia dia l’implementazione del seguente tipo
di dato Persona
public class Persona
{ //OVERVIEW: una Persona e’ caratterizzata dal nome
//(una stringa) e dall’indirizzo (una stringa)
public Persona(String nome,String indirizzo)
{//EFFECTS: costruisce una nuova Persona con nome
// nome ed indirizzo indirizzo}
public String getNome()
{//EFFECTS: restituisce il nome di this }
public String getIndirizzo()
{//EFFECTS: restituisce l’indirizzo di this
}
public String toString()
{//EFFECTS: restituisce una stringa che riporta nome ed
//indirizzo di this}
public boolean equals(Persona p)
{//EFFECTS: restituisce true sse this e’ uguale a p}
}
Implementazione
• La rappresentazione deve essere privata
(e.g. variabili d’istanza private)
• Attenzione al metodo equals, deve
confrontare lo stato interno dei due oggetti e
non il riferimento
Esercizio II
• Si definisca una sottoclasse Studente
di Persona
• le istanze di Studente sono caratterizzate oltre che
dal nome e dall’indirizzo, anche dal numero di matricola
(un intero)
Metodi e Costruttori
•Il costruttore, prende come parametro il nome
e l’indirizzo ed il numero di matricola
•inoltre, vogliamo un metodo aggiuntivo
public int getMatricola(){
//EFFECTS: restituisce la matricola di this }
Cosa fare?
• Progettare la specifica della classe:
intestazione classe=====>
costruttori + metodi + descrizione
informale
Dopo la specifica
• Scegliere le variabili d’istanza adatte a
rappresentare lo stato interno
• Implementare i metodi di conseguenza
Specifica di una sottoclasse
• Deve contenere costruttori e metodi
aggiuntivi rispetto alla superclasse
• Deve contenere i metodi overridden (se
necessario)
• E’ utile ereditare il piu’ possibile
Esempio: specifica costruttore
public Studente(String n,String i, int m)
{//EFFECTS: costruisce un nuovo Studente con nome
// n e indirizzo i e numero di matricola m}
Metodi d’istanza
• Quali metodi possono essere ereditati?
• Quali devono essere sovrascritti?
Metodi della superclasse
public String getNome()
{//EFFECTS: restituisce il nome di this }
public String getIndirizzo()
{//EFFECTS: restituisce l’indirizzo di this
}
FUNZIONANO CORRETTAMENTE!
Metodi della superclasse
public String toString()
{//EFFECTS: restituisce una stringa che riporta nome ed
//indirizzo di this}
NON E’adeguato =========>
OVERRIDING
toString() di Studente
public String toString()
{//EFFECTS: restituisce una stringa che riporta nome ed
//indirizzo e numero di matricola di this}
•Overriding: dobbiamo dichiarare lo stesso metodo (stessa
intestazione), specializzandone specifica ed l’implementazione
Metodi della superclasse
public boolean equals(Persona p)
{//EFFECTS: restituisce true sse this e’ uguale a p}
•Il parametro e’ di tipo Persona
•Il metodo confronta lo stato interno di this con quello
del parametro p
Se non lo sovrascrivo
public boolean equals(Persona p)
{//EFFECTS: restituisce true sse this e’ uguale a p}
•La sottoclasse Studente lo eredita
•Il parametro e’ di tipo Persona (lo posso usare
con un parametro di ogni sottotipo)
Esempio
Persona p1=new Persona(“Fra”,”Pisa”);
Studente s1=new Studente(“Elodie”,”Pisa”,1);
Studente s2=new Studente(“Elodie”,”Pisa”,2);
•Posso chiamarlo per confrontare uno studente ed
una persona
s1.equals(p1)====> false
•Posso chiamarlo per confrontare due studenti
s1.equals(s2)====> true
•In entrambi i casi confronta solo nome ed indirizzo
Per contro
Studente s1=new Studente(“Elodie”,”Pisa”,1);
Studente s2=new Studente(“Elodie”,”Pisa”,2);
•Vorrei
s1.equals(s2)====> false
•Differiscono per il numero di matricola
•Come si risolve il problema?
Soluzione I:overriding
public boolean equals(Persona p)
{//REQUIRES: p di tipo Studente
//EFFECTS: restituisce true sse this e’ uguale a p
(ha stesso nome, indirizzo e numero di matricola)
}
• Precondizione richiede che il parametro sia del
sottotipo (altrimenti la post-condizione non avrebbe
senso)
•Puo’ essere usato solo per confrontare due Studenti
Esempio
Studente s1=new Studente(“Elodie”,”Pisa”,1);
Studente s2=new Studente(“Elodie”,”Pisa”,2);
•Abbiamo
s1.equals(s2)====> false
•Viene selezionato il metodo della sottoclasse perche’
this e’ di tipo Studente (metodo piu’ specifico)
Svantaggio della soluzione
•Non abbiamo garanzia del fatto che equals sovrascritto
sia di tipo Studente (chi lo usa potrebbe sbagliare)
•E’ sempre conveniente che un metodo sovrascritto
funzioni per tutti gli inputs per cui funzionava il
metodo della superclasse
Deve esserci sempre una relazione tra la specifica di una
sottoclasse e quella di una superclasse (lo vedremo ad
MP)
Soluzione II:overloading
//metodo nuovo nella sottoclasse
public boolean equals(Studente p)
{
//EFFECTS: restituisce true sse this e’ uguale a p
(ha stesso nome, indirizzo )}
•Puo’ essere usato solo per confrontare due Studenti
•Dichiariamo un nuovo metodo nella sottoclasse
Overloading
•Nella classe Studenti avremmo due metodi equals
METODO EREDITATO:
public boolean equals(Persona p)
{//EFFECTS: restituisce true sse this e’ uguale a p}
METODO NUOVO:
public boolean equals(Studente p)
{//EFFECTS: restituisce true sse this e’ uguale a p}
•L’interprete sceglie quale eseguire in base al tipo del
parametro
Esempio
Persona p1=new Persona(“Fra”,”Pisa”);
Studente s1=new Studente(“Elodie”,”Pisa”,1);
Studente s2=new Studente(“Elodie”,”Pisa”,2);
•Se il parametro e’ del supertipo usa quello ereditato
s1.equals(p1)====> false
•Se il parametro e’ del sottotipo usa quello nuovo
s1.equals(s2)====> false
•Soluzione migliore: la scelta la fa l’interprete e non il
programmatore
Attenzione: implementazione
• Il costruttore di Studente deve assegnare i
valori a tutte le variabili (nuove ed
ereditate)
• Ereditarieta’ del costruttore: automatica solo
per quello di default (senza parametri)
• Invece nella superclasse il costruttore
chiede dei parametri
Problema
• Variabili della superclasse sono private
• Bisogna usare costruttori e metodi della
superclasse (tramite super)
• Alternativa protected
Principio di Sostituzione
• Un oggetto del sottotipo può essere utilizzato
dovunque sia richiesto un oggetto del supertipo
• Un oggetto di tipo Studente puo’ essere usato
ovunque sia richiesto un oggetto di tipo Persona
• Per capire questo concetto scriviamo del codice
che usa il supertipo Persona
Esercizio III
•Vogliamo realizzare un Archivio
•Memorizza informazioni su un insieme di persone
(oggetti di tipo Persona) di dimensione fissata
•Vogliamo che nell’insieme non ci siano ripetizioni
Specifica I
public class Archivio
{ //OVERVIEW: un Archivio e’ un insieme di Persona
// senza ripetizioni di dimensione fissa
public Archivio(int dim)
{//REQUIRES: dim >0
//EFFECTS: costruisce un nuovo Archivio vuoto
//di dimensione dim}
public String toString()
{//EFFECTS: restituisce una stringa che descrive l’insieme
//di persone contenute in this}
Specifica II
public String cercan(String indirizzo)
{//EFFECTS: restituisce il nome di una Persona con indirizzo
indirizzo che occorre in this,
altrimenti restituisce la stringa vuota}
public String cercai(String nome)
{//EFFECTS: restituisce l’indirizzo di una Persona con nome
nome che occorre in this, altrimenti restituisce la stringa vuota}
Specifica III: modificatori
public boolean insert(Persona p)
{//MODIFIES: this
//EFFECTS: se p non occorre gia’ in this e ci sono ancora
posizioni libere lo inserisce e restituisce true, altrimenti
restituisce false}
public boolean remove(Persona p)
{//MODIFIES: this
//EFFECTS: se p appartiene a this lo rimuove e
restituisce true, altrimenti restituisce true}
}
Implementazione
•Per rappresentare lo stato di un archivio
puo’ usare un array di Persona
•La dimensione dell’array viene fissata al momento
della creazione dell’archivio
•E’ utile memorizzare la prima posizione dell’array
che e’ libera
Implementazione
•Nei metodi di inserimento e rimozione per confrontare
due oggetti Persona usare equals (al solito)
•Le variabili d’istanza che implementano lo
stato devono essere private
•Altrimenti non possiamo garantire che non ci siano
ripetizioni
Testing
• Archivio e’ definito per il supertipo
Persona
• Puo’ memorizzare oggetti di un qualsiasi
sottotipo (ex Studente)
Nel main
Studente x1=new Studente(“Fra”,”Pisa”, 12);
Studente x2=new Studente(“Fra”,”Pisa”,13);
Archivio a= new Archivio(5);
boolean c=a.insert(x1);
boolean c= a.insert(x2);
Per confrontare studenti a run-time viene scelto il metodo
equals piu’ specifico: quello overriden della sottoclasse !
Quello della superclasse non e’ adatto (differiscono solo per il
numero di matricola)
Scarica

Lucidi