Programmazione ad Oggetti con Java: Ereditarietà Ereditarietà Ereditarietà: concetti di base L’ereditarietà è uno dei concetti base della programmazione ad oggetti Concetto di base: l’ereditarietà permette di usare una classe precedentemente definita per la definizione di una nuova L’ereditarietà è una tecnica tale per cui è possibile definire: 2 una classe estremamente generale successivamente classi più specializzate semplicemente aggiungendo nuovi dettagli alla classe di partenza Programmazione ad Oggetti con Java: Ereditarietà 1 Programmazione ad Oggetti con Java: Ereditarietà Ereditarietà: definizione più formale È una relazione fra classi in cui una classe (superclasse o classe base) “raccoglie a fattor comune” le caratteristiche comuni di una o più altre classi (sottoclassi o specializzazioni) Le sottoclassi ereditano tutte le caratteristiche della superclasse In UML si indica graficamente con una freccia dalla sottoclasse alla superclasse VeicoloMotorizzato superclasse o classe base Auto Moto Pulman sottoclassi o specializzazioni 3 Programmazione ad Oggetti con Java: Ereditarietà Astrazione Superclassi Specializzazione Generalizzazione Classi Classificazione Istanziazione Oggetti Rappresentazione Mondo reale 4 Programmazione ad Oggetti con Java: Ereditarietà 2 Programmazione ad Oggetti con Java: Ereditarietà Perché l’Ereditarietà Economia tutti gli attributi, operazioni e associazioni comuni a un certo insieme di classi vengono scritte una sola volta Consistenza se un attributo, operazione o associazione della superclasse viene cambiato, la modifica si ripercuote automaticamente su tutte le sottoclassi Riuso la superclasse può essere riusata in contesti diversi Estendibilità è sempre possibile, anche in un secondo momento, aggiungere nuove sottoclassi senza dover riscrivere la parte comune contenuta nella superclasse ogni sottoclasse può ridefinire a piacimento attributi e operazioni (overriding) 5 Programmazione ad Oggetti con Java: Ereditarietà Fino a dove posso arrivare? Non ho limiti sulla gerarchia che posso costruire Veicolo VeicoloMotorizzato Auto Moto VeicoloNonMotorizzato Pulman Bicicletta Carrozza Taxi 6 Programmazione ad Oggetti con Java: Ereditarietà 3 Programmazione ad Oggetti con Java: Ereditarietà Il tipo di relazione L’ereditarietà realizza una relazione is-a Quindi è corretto affermare che un oggetto creato a partire da una sottoclasse sia di tipo della superclasse! Esempi: un oggetto istanziato a partire dalla classe Bicicletta, oltre che essere una Bicicletta, è anche un VeicoloNonMotorizzato e un Veicolo un oggetto istanziato a partire dalla classe Taxi, oltre che ad essere un Taxi, è anche un’Automobile, un VeicoloMotorizzato e un Veicolo ... 7 Programmazione ad Oggetti con Java: Ereditarietà Ereditarietà multipla Una classe può ereditare da più superclassi (ereditarietà multipla) Esempio: lo studente lavoratore... Studente - Lav oratore nome: String cognome: String matricola: int - nome: String cognome: String numeroPrevidenza: int StudenteLav oratore 8 Programmazione ad Oggetti con Java: Ereditarietà 4 Programmazione ad Oggetti con Java: Ereditarietà Ereditarietà multipla: problemi L’ereditarietà multipla è semplice concettualmente, ma porta a una serie di problemi tecnici: Cosa succede se una classe eredita due volte dalla stessa superclasse? Cosa succede se due superclassi diverse hanno un attributo con lo stesso nome? Persona - nome: String cognome: String Studente - Lav oratore matricola: int - numeroPrevidenza: int StudenteLav oratore Fortunatamente Java non supporta l’ereditarietà multipla 9 Programmazione ad Oggetti con Java: Ereditarietà L’Ereditarietà in Java La sintassi utilizzata in Java per esplicitare che una classe deriva da un’altra è la seguente: public class ClassePadre { ... } public class ClasseFiglia extends ClassePadre { ... } 10 Programmazione ad Oggetti con Java: Ereditarietà 5 Programmazione ad Oggetti con Java: Ereditarietà Esempio Proviamo ad implementare il seguente diagramma di classi Persona Creiamo un’altra classe con main in cui: nessun costruttore e metodo toString() creiamo una persona con nome Mario, cognome Rossi creiamo uno studente con nome Marco, cognome Verdi e matricola 123456 stampiamo lo stato degli oggetti - nome: String cognome: String + + + + setNome(nome :String) : void setCognome(cognome :String) : void getNome() : String getCognome() : String Studente - matricola: int + + setMatricola(matricola :int) : void getMatricola() : int Sorgente ... 11 Programmazione ad Oggetti con Java: Ereditarietà La classe Object In Java qualsiasi classe specializza la classe Object Obj ect + + Fino ad ora, non sapendolo, abbiamo scritto classi che sono specializzazione della classe Object quindi qualsiasi metodo ed attributo definito nella classe Object è ereditato dalle sottoclassi equals(object :Object) : boolean toString() : String Persona - nome: String cognome: String + + + + setNome(nome :String) : void setCognome(cognome :String) : void getNome() : String getCognome() : String Alcuni metodi importanti della classe Object: - matricola: int + + setMatricola(matricola :int) : void getMatricola() : int 12 public boolean equals(Object o) public String toString() Studente Programmazione ad Oggetti con Java: Ereditarietà 6 Programmazione ad Oggetti con Java: Ereditarietà Esempio Se modifichiamo il main dell’esercizio della slide 12 nel modo seguente: ... Non avremo errori di compilazione: public class Test { public static void main(String a[]) { Persona p = new Persona(); p.setNome("Mario"); p.setCognome("Rossi"); su oggetti di tipo Persona è possibile invocare il metodo toString() poiché ereditato dalla classe Object Studente s = new Studente(); s.setNome("Marco"); s.setCognome("Verdi"); s.setMatricola(123456); System.out.println("Persona "+ p.getNome() + " " + p.getCognome()); System.out.println("Studente "+ s.getNome() + " " + s.getCognome() + " " + s.getMatricola()); System.out.println(p.toString()); Output del tipo: } NomeClasse@hashCode } 13 Programmazione ad Oggetti con Java: Ereditarietà Visibilità Quando una classe ne specializza un’altra, eredita tutto a prescidere dalla visibità! Però: se un attributo è definito private nella superclasse non sarà accessibile tramite nome! occorrerà passare attraverso metodi (ovviamente non privati!) se un metodo è definito private nella superclasse, non ci sarà alcun modo per permettere alle sottoclassi di invocarlo Se le sottoclassi hanno la necessità di accedere in modo diretto ad attributi e/o metodi definiti nella superclasse devo utilizzare un’altro tipo di visibilità: protected Esempio: Auto + Taxi 14 Programmazione ad Oggetti con Java: Ereditarietà 7 Programmazione ad Oggetti con Java: Ereditarietà Esempio package a Accede direttamente a: x, y, w, z A # ~ + Accede direttamente a: y, w, z x: y: z: w: int int int int Accede direttamente a: y, w B Su istanze di tipo B, accede direttamente a: y, w, z Esempio: C 1 1 D B b; ... b.y; b.w; b.z; E Su istanze di tipo C, accede direttamente a: w Esempio: C c; ... c.w; package b 15 Programmazione ad Oggetti con Java: Ereditarietà Riepilogo sulle visibilità Il modificatore di visibilità si applica ad attributi e metodi con lo stesso significato: Visibilità Chi può accedere public la classe in cui è definito tutte le classi (sottoclassi, le classi in associazione, classi nello stesso package, classi in package diversi) protected la classe in cui è definito le sottoclassi le classi appartenenti allo stesso package private la classe in cui è definito le classi appartenenti allo stesso package 16 Programmazione ad Oggetti con Java: Ereditarietà 8 Programmazione ad Oggetti con Java: Ereditarietà Costruttori Cosa succede per i costruttori che accettano in ingresso parametri? Regola da rispettare: ogni classe inizializza i propri attributi! ritornando all’esempio della Persona e della classe Studente ma uno studente è caratterizzato anche dal possedere un nome e un cognome dal momento che è una specializzazione della classe Persona il/i costruttori della classe Persona inizializzeranno gli attributi nome e cognome il/i costruttori della classe Studente inizializzeranno l’attributo matricola quindi, quando si crea uno studente, occorre inizializzare anche nome e cognome Come si fa? 17 Programmazione ad Oggetti con Java: Ereditarietà Costruttori Esistono 2 modi: Meno elegante: Più elegante: nel/nei costruttori della sottoclasse si inizializzno i valori per tutti gli attributi si invoca il costruttore della superclasse passando come argomenti i valori per gli attributi li definiti per fare ciò si utilizza la keyword super L’invocazione al costruttore della superclasse deve essere la prima istruzione ad essere eseguita 18 quindi non ci possono essere contemporaneamente super e this Programmazione ad Oggetti con Java: Ereditarietà 9 Programmazione ad Oggetti con Java: Ereditarietà Regole Sono sempre obbligato ad invocare esplicitamente il costruttore della superclasse? No: se la superclasse esporta il costruttore di default Si: se la superclasse esporta esclusivamente costruttori con parametri Anche se io non invoco esplicitamente il costruttore della superclasse, Java inserisce all’interno dei costruttori definiti nella sottoclasse l’invocazione al costruttore di default della superclasse ciò comporta che il primo costruttore ad essere eseguito è sempre quello della classe Object ciò è valido se il costruttore non ha l’invocazione this! 19 Programmazione ad Oggetti con Java: Ereditarietà Implementazione relazione is-a Ricordandoci che l’ereditarietà realizza una relazione is-a, è lecito scrivere: SuperClasse ref = new SottoClasse(); Ma non è lecito scrivere: SottoClasse ref = new SuperClasse(); 20 Programmazione ad Oggetti con Java: Ereditarietà 10 Programmazione ad Oggetti con Java: Ereditarietà Esempi Ok Persona s1 = new Studente(); Studente s3 = new Persona(); Ok Object s2 = new Studente(); Ok Object p1 = new Persona(); Ok s1.setNome(“Paperone”); s2.setCognome(“De Paperoni”); Ok p1.toString(); 21 Programmazione ad Oggetti con Java: Ereditarietà Ancora un esempio Realizziamo il seguente diagramma di classi: Animale - String nome + Animale() + void dormi() + void mangia() Gatto Papera Cane + Gatto() + void faiLeFusa() + void parla() + Papera() + void parla() + Cane() + void ringhia() + void parla() Creiamo una classe in cui definiamo un array di 3 animali ed inseriamo un gatto, un cane ed una papera Per ciascuno di essi invochiamo rispettivamente: dormi, parla e mangia 22 Programmazione ad Oggetti con Java: Ereditarietà 11 Programmazione ad Oggetti con Java: Ereditarietà Il cast Permette di convertire un reference ad un tipo inferiore in gerarchia di parentela Sintassi: NomeClasseFiglia ref2 = (NomeClasseFiglia)ref1; Esempio: Animale a = new Gatto(“Miguel”); ... Gatto g = (Gatto)a; g.faiLeFusa(); Meglio se si effettua il controllo prima: if (a instanceof Gatto) { Gatto g = (Gatto)a; g.faiLeFusa(); } 23 Programmazione ad Oggetti con Java: Ereditarietà Concetti avanzati: classi astratte Una classe può essere astratta questo significa che non corrisponde a un concetto concreto del dominio applicativo, ma che è fatta apposta per funzionare da superclasse esempio: non esiste un veicolo, esiste un auto, una moto, ... Un classe astratta: non può essere instanziata ha senso solo in quanto ne vengono create delle sottoclassi Una classe deve essere dichiarata astratta se definisce almeno un metodo astratto Una classe può essere dichiarata astratta anche se non definisce metodi astratti 24 Programmazione ad Oggetti con Java: Ereditarietà 12 Programmazione ad Oggetti con Java: Ereditarietà Sintassi Classe astratta public abstract class NomeClasse { ... } Metodo astratto: [mod. visibilità] abstract void nomeMetodo([parametri]); 25 Programmazione ad Oggetti con Java: Ereditarietà L’abstract si propaga Sono costretta a definire astratte anche le sottoclassi che non definiscono il comportamento di metodi astratti ereditati Animale - String nome Esempio: 26 + Animale() + void dormi() + void mangia() + void parla() Felino Papera Cane + Felino() + Papera() + Cane() + void ringhia() Leone Gatto + Leone() + Gatto() + void faiLeFusa() Programmazione ad Oggetti con Java: Ereditarietà 13 Programmazione ad Oggetti con Java: Ereditarietà L’ereditarietà multipla? Effettivamente in java la si realizza grazie all’utilizzo delle interface L’interface realizza quasi un’associazione del tipo “is-a” al test instanceof restituisce true o false Le interface contengono solo intestazioni di metodi e al più definizioni di costanti Sintassi: public interface NomeInterface { // definizione nomi metodi } public class Classe implements NomeInterface { // definizione del corpo dei metodi ereditati } 27 Programmazione ad Oggetti con Java: Ereditarietà Esempi di ereditarietà: 28 Slide successiva: equals Programmazione ad Oggetti con Java: Ereditarietà 14 Programmazione ad Oggetti con Java: Ereditarietà Confrontare lo stato di 2 oggetti L’operatore == applicato a 2 reference permette di testare se essi referenziano lo stesso oggetto Ma se volessi sapere se due oggetti si trovano nello stesso stato, come faccio? effettuo una comparazione fra i valori degli attributi Modalità classica: 29 overridare il metodo equals della classe Object intestazione: public boolean equals(NomeClasse obj) Programmazione ad Oggetti con Java: Ereditarietà 15