Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’...

46
Ereditarieta’

Transcript of Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’...

Page 1: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Ereditarieta’

Page 2: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Contenuti

• Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’

• Permette di estendere classi gia’ definite (ovvero di definire sottotipi di tipi gia’ definiti)

• Diamo i concetti principali (la semantica formale la vedremo in seguito)

Page 3: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Ricordiamo che

I programmi in Java consistono di classi.

Le classi consentono di definire:

• collezioni di procedure (metodi statici)

• come prototipi di oggetti (tramite variabili e metodi d’istanza, che definiscono il loro stato interno e le operazioni)

Page 4: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Ricordiamo che……

Variabili e metodi statici appartengono alla classe

Variabili e metodi di istanza appartengono agli

oggetti (istanze della classe).

Una classe definisce un tipo (nome della classe).Gli oggetti istanza della classe hanno quel tipo.

Page 5: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

L’ ereditarieta’ è un meccanismo fondamentale sia per il riutilizzo del codice che per lo sviluppo incrementale di programmi.

Questo meccanismo permette infatti di • estendere e potenziare classi già esistenti•fattorizzare informazioni comuni a piu’ classi

Ereditarietà

Page 6: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Supponiamo di volere definire• una classe i cui oggetti hanno una struttura più ricca di quella di una classe già definita•una classe che realizza delle funzionalità aggiuntiverispetto ad una classe già definita

In questi casi si può definire la nuova classe come sottoclasse della precedente, ereditando le caratteristiche gia’ presenti.

A cosa serve

Page 7: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

class Persona {    public String nome;    public indirizzo;

  public Persona()    

{this.nome = "";      this.indirizzo = "";    }    public Persona(String nome,String indirizzo)

     {this.nome = nome;       this.indirizzo = indirizzo;    }    public String getNome()        {return nome;    }     

Esempio di classe 1

Page 8: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

public String getIndirizzo()    {return indirizzo;    }public void visualizza()    {System.out.println("Nome: " + nome +               "\nIndirizzo: " + indirizzo);   }

  public boolean omonimo(Persona p)    {return this.nome.equals(p.nome);   }  }

Esempio di classe 2

Page 9: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Parentesi

• Come si realizza il confronto tra oggetti?

• Semantica di ==

• Semantica di equals (qui usato per confronatare stringhe

• Lo vedremo

Page 10: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Vogliamo definire una classe Studente che rappresenti  gli studenti iscritti ad un corso di laurea. Ogni studente è descritto dal nome, dall'indirizzo, dal numero di matricola e dal piano di studio.

Uno Studente è un tipo particolare di  Persona.

L'ereditarietà ci consente di definire la classe Studente senza ripetere la descrizione di tutte le variabili e i metodi di Persona, ma in modo incrementale, come sottoclasse.

Esempio di sottoclasse 1

Page 11: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

class Studente extends Persona { public int matricola;    public String pianoDiStudio;    static int nextMatricola = 1;

public Studente()     { this.matricola = nextMatricola; nextMatricola=nextMatricola+1;      this.pianoDiStudio = "";    }

public Studente(String nome, String indirizzo)     {this.nome = nome;       this.indirizzo = indirizzo;       this.matricola = nextMatricola; nextMatricola= nextMatricola+1;       this.pianoDiStudio = "";    }

  

Esempio di sottoclasse 2

Page 12: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

  

public String getPdS()     {return pianoDiStudio;    }

   public void modificaPdS(String nuovoPdS)     { pianoDiStudio += nuovoPdS + "\n";    } }

Esempio di sottoclasse 3

Page 13: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

La parola chiave extends significa che

Studente è una sottoclasse o classe derivata di PersonaPersona è una superclasse o classe genitrice di Studente

Analogamente

Studente è un sottotipo di Persona

Page 14: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

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)

Semantica informale

Page 15: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Anche per i costruttori esiste un meccanismo di ereditarietà:

•all’atto della creazione di una istanza di c1 si esegue automaticamente il costruttore di c2 (per inizializzare le variabili ereditate)

Costruttori

Page 16: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

•Un oggetto di tipo Studente avrà quattro variabili di istanza:

*nome e indirizzo ereditate da Persona*matricola e pianoDiStudio definite nella sottoclasse Studente

•Su di lui possono essere invocati tutti i metodi della sottoclasse e della superclasse

Page 17: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Un oggetto di tipo Studente

Page 18: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Esempio

Studente p= new Studente();p.getIndirizzo(); (metodo della superclasse)

p.getPds(); (metodo della sottoclasse)

p.nome; (variabile della superclasse)

p.matricola; (variabile della sottoclasse)

•Le variabili sono pubbliche (possiamo anche essere fuori dalla classe

Page 19: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Esempio: costruttori

public Studente()     { this.matricola = nextMatricola ++;       this.pianoDiStudio = "";    }

Il costruttore di Persona viene invocato automaticamente per inizializzare le variabili eredidate alla stringa vuota.

public Persona()  {this.nome = "";

     this.indirizzo = "";    }

Page 20: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Una sottoclasse puo’ riscrivere un metodo della superclasse (stesso nome, stessi parametri, stesso tipo)

In tal caso sugli oggetti della sottoclasse viene utilizzato il metodo riscritto (quello piu’ specifico)

Overriding

Page 21: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Ad esempio, se invochiamo il metodo visualizza su un'istanza di Studente, verranno stampati solo i valori delle prime due variabili d'istanza (nome e indirizzo).

public void visualizza()    {System.out.println("Nome: " + nome +               "\nIndirizzo: " + indirizzo);   }

Page 22: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Se vogliamo stampare anche la matricola ed il piano di studio,possiamo sovrascrivere (override) visualizza aggiungendo a Studente il seguente metodo:

public void visualizza()    {System.out.println("Nome: " + nome +  "\nIndirizzo: " + indirizzo); System.out.println("Matricola: " + matricola +           "\nPianodiStudio: " + pianoDiStudio);    }

Page 23: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Il comando

p.visualizza()

invocherà •il metodo visualizza della classe Persona se p è un'istanza di Persona•il nuovo metodo che stampa anche il numero di matricola e il piano di studio se p è un'istanza di Studente.

•La scelta del metodo piu’ specifico viene effettuata a tempo di esecuzione

•Non puo’ essere fatta a tempo di compilazione (come vedremo a causa dei sottotipi)

Page 24: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Ricordiamo che le variabili private sono visibili solo nella classe in cui sono dichiarate

Se nella classe Persona avessimo dichiarato le variabilid'istanza private, non sarebbe stato possibile accedere dalla sottoclasse alle variabili private dichiarate nella superclasse.

Problemi nei costruttori e nel metodo visualizza()  (sovrascritto)  

Specificatori di accesso : private e public

Page 25: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Il metodo visualizza di Studente avrebbe causato un errore in compilazione, tentando di 

accedere a variabili private dichiarate nella superclasse. 

public void visualizza()    {System.out.println("Nome: " + nome +                           "\nIndirizzo: " + indirizzo);       System.out.println("Matricola: " + matricola +                           "\nPianodiStudio: " + pianoDiStudio);    }

Visibilita’ dei nomi

Page 26: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Allora come avrebbe fatto un oggetto di tipo Studente a modificare le variabili d’istanza ereditate dalla superclasse?

Si puo’ accedere alle variabili attraverso i metodi della superclasseutilizzando super

super (simile a this) fa riferimento all'istanza che sta eseguendo un metodo o un costruttore, ma costringe l'interprete a vedere l'oggetto come istanza della superclasse.

Super 1

Page 27: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Riscriviamo il metodo visualizza per Studente in modo da chiamare il metodo visualizza di Persona per accedere alle variabili della superclasse

public void visualizza()  {    super.visualizza();    System.out.println("Matricola: " + matricola +                        "\nPianodiStudio: " + pianoDiStudio); }

Esempio

Page 28: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Gerarchia di Tipi

• La relazione di sottoclasse e’ transitiva• Si crea una gerarchia di classi (o di tipi) al cui top

c’e’ la classe Object.• Tutti i tipi che definiamo sono per default sottotipi

di Object• Vedremo alcuni metodi di Object nelle prossime

lezioni

Page 29: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Controllo dei tipi ed ereditarieta’

• L’ereditarieta’ e’ un meccanismo molto potente per fattorizzare e riusare il codice

• Si creano pero’ delle problematiche per I controlli statici (quelli effettuati dal compilatore, tipi, regole di scoping)

Page 30: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Grazie all’ereditarieta’ i sottotipi supportono il comportamento del supertipo, ovvero le istanze della sottoclasse

• hanno le variabili d’istanza ed i metodi (al limite overridden) del supertipo

•hanno accesso alle variabili ed ai metodi statici della superclasse

Di conseguenza un oggetto del sottotipo può essere utilizzato dovunque sia richiesto un oggetto del supertipo.

Principio di sostituzione

Page 31: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Persona tizio = new Studente("Mario Rossi", "Pisa");

/* corretto: su tizio posso invocare tutti i metodi di Persona, grazie all'ereditarieta' */

tizio.visualizza() ; tizio.nome=“Francesca”;

     

Esempio 1Un'istanza di Studente si può usare dovunque sia richiesto un oggetto di Persona, come in un assegnamento o nel passaggio di parametri.

Page 32: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Un'istanza di Studente si può usare dovunque sia richiesto un oggetto di Persona, come in un assegnamento o nel passaggio di parametri.

Studente pippo = new Studente("Mario Rossi", "Pisa");

/* corretto: omonimo richiede un parametro di tipo Persona */

... if(tizio.omonimo(pippo))       ...

Esempio

Page 33: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Non è possibile il contrario, ovvero utilizzare un oggetto del supertipo al posto di uno del sottotipo.

Il sottotipo puo’ avere variabili e metodi (d’istanza o statici) aggiuntivi, per esempio il numero di matricola o il metodo che ritorna il piano di studio.

... Studente tizio = new Persona("Mario Rossi", "Pisa");

/* errore di tipo/

tizio.getpdS();

/* altrimenti potrei fare una invocazione inesistente/

Attenzione:

Page 34: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

•Il principio di sostituzione e’ fondamentale per sfruttare i sottotipi•Permette di riusare tutto il codice definito per il sottotipo•Permette di realizzare una sola volta operazioni comuni tra vari sottotipi

•Ci sono pero’ dei problemi legati alla presenza di sottotipi

...

Flessibilita’

Page 35: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

•Tipo apparente : tipo con cui una variabile e’ dichiarata

Tipo effettivo: tipo del valore legato alla variabile

Il tipo apparente ed il tipo effettivo possono essere diversi,in particolare il tipo effettivo puo’ essere un sottotipo del tipo apparente

...

Tipo Apparente e Tipo Effettivo

Page 36: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

tizio ha

•tipo apparente Persona •tipo effettivo Studente

Persona tizio = new Studente("Mario Rossi", "Pisa”);

...

Esempi

Page 37: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Problemi

Persona tizio = new Studente("Mario Rossi","Pisa"); tizio.modificaPdS("Algebra"); 

•Alcune operazioni legali rispetto al tipo effettivo possono essere illecite rispetto al tipo apparente (che e’ un supertipo di quello effettivo)

•La chiamata di modificaPdS non e’ corretta rispetto al tipo apparente(Persona non ha un metodo con quel nome)

Page 38: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Di conseguenza

• Il controllo dei tipi in Java e’ effettuato staticamente dal compilatore (non a run-time, ma prima)

• il compilatore di Java effettua una analisi statica, ovvero una verifica sul codice del programma, che esamina tutte le istruzioni separatamente e controlla che ogni assegnamento e ogni chiamata di metodo (passaggio di parametri) sia corretta rispetto ai tipi delle variabili

• Come fa il compilatore a conoscere il tipo effettivo di una variabile senza eseguire un programma?

• Non puo’ (dipende dal flusso di esecuzione)

• Di consequenza il controllo dei tipi fatto dal compilatore

usa solo il tipo apparente

Page 39: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Persona tizio = new Studente("Mario Rossi", "Pisa"); tizio.modificaPdS("Algebra");    Si verifica un errore di compilazione. Infatti abbiamo chiamato su tizio (variabile dichiarata di classe Persona) un metodo della sottoclasse Studente.

Anche se il tipo effettivo sarebbe giusto……il compilatore non lo conosce, non puo’ conoscere il valore che puo’ assumere una variabile!

Esempio 1

Page 40: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

 In certe situazioni è utile/necessario invocare su di un identificatore un metodo di una sottoclasse. In questi casi si può usare l'operazione di cast

Persona tizio = new Studente("Mario Rossi", "Pisa"); ((Studente)tizio).modificaPdS("Algebra");     

 Non ci sono errori in fase di compilazione, il compilatore verifica solo che il tipo apparente di tizio sia sottotipo di Studente (tipo verso il quale facciamo il cast) e tratta nella chiamata di metodo tizio come se fosse del sottotipo

Cast 1

Page 41: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Quando si valuta ((Studente) tizio) se tizio non ha tipo effettivoStudente, verrà invece sollevata una eccezione a run-time

Si può controllare la classe di appartenenza di un oggetto prima del cast:

if (tizio instanceof Studente)       ((Studente) tizio).modificaPdS("Algebra");

La condizione (obj instanceof Classe) restituisce true se e solo se obj è una istanza della classe Classe .

Cast 2

Page 42: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Tipo effettivo e apparente:conseguenza

• Il controllo dei tipi statico non evita errori di tipo a run-time a causa dei cast

• La scelta del metodo piu’ specifico tra metodi overridden dipende dal tipo effettivo (deve essere fatta a run-time)

Page 43: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

Esempio

• Supponiamo di avere riscritto il metodo visualizza nella sottoclasse studente

Persona tizio = new Studente("Mario Rossi", "Pisa"); tizio.visualizza();     

•Compila correttamente

•Deve invocare il metodo piu’ specifico

(quello della sottoclasse) in base al tipo effettivo

Page 44: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

• Ricordiamo che l’analisi statica effettuata dal compilatore verifica anche le regole di scoping

•anche queste vengono realizzate rispetto al tipo apparente

•Le regole sono quelle che abbiamo visto dove per I sottotipivale la seguente idea.

Visibilita’ dei nomi e regole di scoping

Page 45: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

• Da una classe sono direttamente visibili nell’ordine i propri nomi statici e quelli delle superclassi

•I metodi statici hanno la visibilita’ della classe di appartenenza (quella in cui sono dichiarati) oltre ai nomi locali

Non vediamo i nomi d’istanza, infatti non ci sono oggetti di riferimento

Variabili + metodi statici

Page 46: Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.

• Da un oggetto sono direttamente visibili nell’ordinei propri nomi d’istanza (inclusi quelli ereditati) e la classe, tramite questa le variabili statiche sue e delle superclassi

•I metodi d’istanza (inclusi i costruttori) hanno la visibilita’ dell’oggetto su cui sono eseguiti

•Infatti le variabili d’istanza appartengono agli oggetti, e le variabili statiche sono condivise tra tutti gli oggetti della classe

Variabili + metodi d’istanza