La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

Ereditarietà. Concetti principali Ereditarietà e (overriding) di metodi Dynamic dispatch e polimorfismo Ereditarietà e costruttori Livelli di accesso.

Presentazioni simili


Presentazione sul tema: "Ereditarietà. Concetti principali Ereditarietà e (overriding) di metodi Dynamic dispatch e polimorfismo Ereditarietà e costruttori Livelli di accesso."— Transcript della presentazione:

1 Ereditarietà

2 Concetti principali Ereditarietà e (overriding) di metodi Dynamic dispatch e polimorfismo Ereditarietà e costruttori Livelli di accesso protected e package La classe Object metodi toString, equals e clone

3 Ereditarietà – introduzione Un meccanismo per estendere classi con nuovi campi e nuovi metodi Esempio: dato un conto bancario, vogliamo definire un conto con interessi SavingsAccount (~ libretto di risparmio) Continua… class SavingsAccount extends BankAccount { nuovi metodi nuovi campi }

4 Ereditarietà – introduzione SavingsAccoun t eredita metodi e campi di BankAccount Terminologia superclasse = classe che viene estesa ( BankAccount ) sottoclasse = classe che estende ( Savings ) Continua… SavingsAccount collegeFund = new SavingsAccount(10); // Conto con il 10% tasso di interesso collegeFund.deposit(500); // Possiamo usare un metodo di BankAccount // anche su oggetti di tipo SavingsAccount

5 Diagrammi UML SuperClasse Classe Interfaccia Classe

6 Diagrammi UML La relazione di sottoclasse è transitiva Ogni class estende la classe Object, direttamente o indirettamente

7 Superclassi vs interfacce Estendere una classe è diverso da implementare una interfaccia Entrambi i meccanismi generano sottotipi Interfacce rappresentano le classi che le implementano Classi rappresentano le proprie sottoclassi Ma … Una sottoclasse eredita limplementazione dei metodi ed i campi della superclasse riuso di codice

8 Ereditarietà – sottoclassi Nellimplementazione di una sottoclasse definiamo solo ciò che cambia rispetto alla superclasse

9 Ereditarietà – sottoclassi SavingsAccoun t implementa le differenze rispetto a BankAccount public class SavingsAccount extends BankAccount { public SavingsAccount(double rate) { interestRate = rate; } public void addInterest() { double interest = getBalance() * interestRate / 100; deposit(interest); } private double interestRate; }

10 Domanda Quali metodi possono essere invocati su oggetti di tipo SavingsAccount ?

11 Risposta Tutti i metodi ereditati: deposit(), withdraw(), getBalance() Il metodo addInterest() definito nella sottoclasse

12 Ereditarietà – this addInterest() invoca deposit() senza specificare un oggetto linvocazione riguarda this, listanza della sottoclasse che eredita i metodi della superclasse

13 Ereditarietà – encapsulation Le sottoclassi non hanno accesso ai campi privati della superclasse SavingsAccount non può riferire direttamente il campo balance Può usare i metodi ereditati dalla superclasse addInterest() accede a balance con i metodi getBalance() e deposit()

14 Istanze di sottoclasse Un oggetto di tipo SavingsAccount eredita il campo balance da BankAccount, ed ha un campo aggiuntivo, interestRate :

15 Domanda Date due classi Manager e Employee, quale delle due definireste come superclasse e quale come sottoclasse?

16 Risposta Employer Manager

17 Gerarchie di classi Le applicazioni sono spesso formate come gerarchie complesse di classi in relazione di ereditarietà

18 Gerarchie di classi – Swing Continua…

19 Gerarchie di classi – Swing La superclasse JComponent definisce due metodi getWidth, getHeight ereditati da tutte le classi nella gerarchia La classe AbstractButton ha metodi set/get per definire e accedere alla etichetta o icona associata al pulsante

20 Domanda Quale è lo scopo della classe JTextComponent nella gerarchia Swing?

21 Risposta Implementa gli aspetti comuni del comportamento delle classi JTextField e JTextArea

22 Una gerarchia di conti bancari Continua…

23 Una gerarchia di conti bancari Continua… BankAccount realizza le funzionalità di base di un conto bancario CheckingAccount : non offre interessi, ma permette un numero di transazioni gratuite e per le altre commissioni basse SavingsAccount : interessi calcolati su base mensile e transazioni con commissione

24 Una gerarchia di conti bancari Tutti i tipi di conto offrono il metodo getBalance() Tutti i tipi di conto offrono deposit() e withdraw(), Implementazione diversa nei diversi tipi CheckingAccount include un metodo deductFees() per dedurre le commissioni SavingsAccount include un metodo addInterest() per il calcolo degli interessi

25 Ereditarietà – metodi Tre scelte possibili nel progetto di una sottoclasse ereditare i metodi della superclasse modificare i metodi della superclasse ( overriding ) definire nuovi metodi / nuove versioni dei metodi ereditati Continua…

26 Ereditarietà – metodi ereditati Metodi della superclasse visibili nella sottoclasse Non ridefiniti nella sottoclasse I metodi della superclasse possono essere sempre invocati su oggetti della sottoclasse Quando invocati su oggetti della sottoclasse this punta alloggetto della sottoclasse Il tipo di this cambia, a seconda delloggetto a cui this viene legato Continua…

27 Ereditarietà – metodi aggiuntivi Le sottoclassi possono definire nuovi metodi, non definiti nella superclasse I nuovi metodi sono invocabili solo su oggetti della sottoclasse

28 Ereditarietà – overriding di metodi Forniscono una implementazione diversa di un metodo che esiste nella superclasse Il metodo nella sottoclasse deve avere la stessa firma del metodo nella superclasse stesso nome/tipo risultato, stessi tipi dei parametri, livello di accesso maggiore o uguale Continua…

29 Ereditarietà – variabili di istanza Tutti i campi di una superclasse sono automanticamente ereditati nella sottoclasse Ma, se privati, non possono essere acceduti direttamente dalla sottoclasse! La sottoclasse può definire campi che non esistono nella superclasse Continua…

30 Ereditarietà – variabili di istanza Nel caso di collisione di nomi … La definizione del campo della sottoclasse maschera quella del campo nella superclasse Collisioni di nomi legali ma decisamente inopportune (… vedremo)

31 Una gerarchia di conti bancari Continua…

32 La classe CheckingAccount Sovrascrive ( overrides ) i metodi deposit() e withdraw() per incrementare il contatore delle operazioni: Continua… public class CheckingAccount extends BankAccount { public void deposit(double amount) {...} public void withdraw(double amount) {...} public void deductFees() {...} // nuovo metodo private int transactionCount; // nuovo campo }

33 La classe CheckingAccount due variabili di istanza: Balance, ereditato da BankAccount transactionCount, nuovo Continua…

34 La classe CheckingAccount Quattro metodi getBalance() ereditato da BankAccount deposit(double amount) sovrascrive il metodo corrispondente di BankAccount withdraw(double amount) sovrascrive il metodo corrispondente di BankAccount deductFees() nuovo

35 Accesso ai campi della superclasse Consideriamo il metodo deposit() della classe CheckingAccount public void deposit(double amount) { transactionCount++; // aggiungi amount a balance... } Continua…

36 Accesso ai campi della superclasse Consideriamo il metodo deposit() della classe CheckingAccount Non possiamo modificare direttamente balance balance è un campo privato della superclasse public void deposit(double amount) { transactionCount++; // aggiungi amount a balance balance += amount;// ERRORE! } Continua…

37 Accesso ai campi della superclasse Una sottoclasse non ha accesso ai campi / metodi private della superclasse Deve utilizzare linterfaccia public (o protected vedremo …) della superclasse Nel caso in questione, CheckingAccount deve invocare il metodo deposit() della superclasse

38 Accesso ai campi della superclasse Errore tipico: aggiungiamo un nuovo campo con lo stesso nome: Continua… public class CheckingAccount extends BankAccount { public void deposit(double amount) { transactionCount++; balance = balance + amount; }... private double balance; // Brutta idea! }

39 Accesso ai campi della superclasse Ora il metodo deposit compila correttamente ma non modifica il saldo corretto! Continua…

40 Accesso ai campi della superclasse Soluzione, accediamo i campi della superclasse via i metodi della stessa superclasse Continua… public class CheckingAccount extends BankAccount { public void deposit(double amount) { transactionCount++; balance = balance + amount; deposit(amount); }... }

41 Chiamata di un metodo overridden Non possiamo invocare direttamente deposit(amount) …. È come invocare this.deposit(amount) Continua… class CheckingAccount public {... void deposit(double amount) { transactionCount++; balance = balance + amount; deposit(amount); // questo è un loop! }... }

42 Chiamata di un metodo overridden Dobbiamo invece invocare il metodo deposit() della superclasse Invochiamo via super public void deposit(double amount) { transactionCount++; balance = balance + amount; super.deposit(amount); }

43 Sintassi: chiamata via super super.methodName(parameters) Esempio: public void deposit(double amount) { transactionCount++; super.deposit(amount); } Scopo: Invocare un metodo overridden della superclasse

44 La classe CheckingAccount public class CheckingAccount extends BankAccount {... public void withdraw(double amount) { transactionCount++; // sottrai amount da balance super.withdraw(amount); } Continua…

45 La classe CheckingAccount public void deductFees() { if (transactionCount > FREE_TRANSACTIONS) { double fees = TRANSACTION_FEE * (transactionCount - FREE_TRANSACTIONS); super.withdraw(fees); } transactionCount = 0; }... private static final int FREE_TRANSACTIONS = 3; private static final double TRANSACTION_FEE = 2.0; } Definizione dei metodi aggiuntivi

46 Domanda Perchè il metodo withdraw() nella classe CheckingAccount invoca super.withdraw() ? È possibile invocare un metodo della supeclasse senza utilizzare il riferimento super ? Perchè il metodo deductFees() nella classe CheckingAccount invoca super.withdraw() ?

47 Risposte Perché deve modificare la variabile balance e non può farlo direttamente in quanto è privata della superclasse n Si se la sottoclasse non ridefinisce il metodo Per evitare che il prelievo dellimporto delle commissioni venga contato come operazione.

48 Ereditarietà (ancora)

49 Costruttori di sottoclasse Il costruttore di una sottoclasse provvede ad inizializzare la struttura delle istanze della sottoclasse Come abbiamo visto, questa include la parte definita nella superclasse Per inizializzare i campi privati della superclasse invochiamo il costruttore della superclasse Continua…

50 Istanze di sottoclasse Un oggetto di tipo SavingsAccount eredita il campo balance da BankAccount, ed ha un campo aggiuntivo, interestRate :

51 Costruttori di sottoclasse Per invocare il costruttore della superclasse dal costruttore di una sottoclasse usiamo la parola chiave super seguita dagli argomenti Deve essere il primo comando del costruttore della sottoclasse Continua…

52 Costruttori di sottoclasse NB: non confondere con la chiamata di metodo della superclasse super.m(…) Continua… class SavingsAccount extends BankAccount { public SavingsAccount(double balance, double ir) { // Chiamata al costruttore di superclasse super(balance); // inizializzazioni locali interestRate = ir; }... }

53 Costruttori di sottoclasse Se il costruttore di sottoclasse non invoca esplicitamente il costruttore di superclasse, il compilatore inserisce la chiamata super() al costuttore di default (senza parametri) Se tutti i costruttori della superclasse richiedono parametri, errore di compilazione

54 Conversioni di tipo Una sottoclasse è un sottotipo della sua superclasse Possiamo assegnare ad una variabile di classe un riferimento di tipo sottoclasse SavingsAccount collegeFund = new SavingsAccount(10); BankAccount anAccount = collegeFund; Object anObject = collegeFund;

55 Conversioni di tipo I tre riferimenti contenuti in collegeFund, anAccount e anObject riferiscono tutti lo stesso oggetto, di tipo SavingsAccount

56 Conversioni di tipo Utilizzare un riferimento di tipo superclasse causa una perdita di informazione BankAccount anAccount = new SavingsAccount(10); anAccount.deposit(1000); // OK // deposit() e un metodo di BankAccount anAccount.addInterest(); // Compiler Error // addInterest() non e un metodo di BankAccount

57 Cast Possiamo recuperare informazione utilizzando cast Attenzione: se anotherAccount non punta ad un SavingsAccount ClassCastException a run BankAccount anAccount = new SavingsAccount(10); anAccount.deposit(1000); // OK // deposit() e un metodo di BankAccount ((SavingsAccount)anAccount).addInterest(); // OK

58 Conversioni di tipo Soluzione: utilizzo delloperatore instanceof verifica se anAccount punta ad un oggetto di tipo SavingsAccount (o di un sottotipo) if (anAccount instanceof SavingsAccount) { ((SavingsAccount)anAccount).addInterest(); // OK... }

59 Conversioni di tipo Nulla di nuovo … stesse idee e meccanismi visti quando abbiamo parlato delle interfacce.

60 Polimorfismo Questo codice può essere utilizzato per trasferimenti su qualunque BankAccount public void transfer(double amount, BankAccount other) { withdraw(amount); other.deposit(amount); }

61 Polimorfismo – dynamic dispatch Meccanismo già discusso per le interfacce Ogni variabile ha due tipi Statico: il tipo dichiarato Dinamico: il tipo delloggetto a cui la variabile riferisce Il metodo invocato da un messaggio dipende dal tipo dinamico della variabile, non dal tipo statico Continua…

62 Polimorfismo – dynamic dispatch Il compilatore verifica che esista un metodo da selezionare in risposta al messaggio se non esiste errore se esiste decide la firma del metodo da invocare Il corpo del metodo, con il tipo selezionato, viene determinato a run time.

63 Polimorfismo – dynamic dispatch Dynamic dispatch al lavoro La selezione (dispatch) dei metodi withdraw() e deposit() da eseguire dipende dal tipo dinamico di this e di other, rispettivamente public void transfer(double amount, BankAccount other) { withdraw(amount); // this.withdraw(amount) other.deposit(amount); } Continua…

64 Polimorfismo – dynamic dispatch invoca SavingsAccount.withdraw() su sa CheckingAccount.deposit() su ca public void transfer(double amount, BankAccount other) { withdraw(amount); // this.withdraw(amount) other.deposit(amount); } Continua… BankAccount sa = new SavingsAccount(10); BankAccount ca = new CheckingAccount(); sa.transfer(1000, ca);

65 File BankAccount.java 01: /** 02: La classe radice della gerarchia 03: 04: */ 05: public class BankAccount 06: { 07: /** 08: Constructs a bank account with a zero balance. 09: */ 10: public BankAccount() 11: { 12: balance = 0; 13: } 14: 15: /** 16: Constructs a bank account with a given balance. 17: @param initialBalance the initial balance 18: */ Continua…

66 File BankAccount.java 19: public BankAccount(double initialBalance) 20: { 21: balance = initialBalance; 22: } 23: 24: /** 25: Peposita un importo sul conto. 26: @param amount limporto da depositare 27: */ 28: public void deposit(double amount) 29: { 30: balance = balance + amount; 31: } 32: 33: /** 34: Prelievo di un importo dal conto. 35: @param amount limporto da prelevare 36: */ Continua…

67 File BankAccount.java 37: public void withdraw(double amount) 38: { 39: balance = balance - amount; 40: } 41: 42: /** 43: Saldo corrente del conto. 44: @return il valore del campo balance 45: */ 46: public double getBalance() 47: { 48: return balance; 49: } 50: 51: /** 52: Trasferimento da questo conto ad un altro conto 53: @param amount limporto da trasferire 54: @param other il conto su cui trasferire 55: */ Continua…

68 File BankAccount.java 56: public void transfer(double amount, BankAccount other) 57: { 58: withdraw(amount); 59: other.deposit(amount); 60: } 61: 62: private double balance; 63: }

69 File CheckingAccount.java 01: /** 02: Un conto corrente, con commissioni sulle operazioni 03: */ 04: public class CheckingAccount extends BankAccount 05: { 06: /** 07: Costruisce un conto con un saldo iniziale. 08: @param initialBalance il saldo iniziale 09: */ 10: public CheckingAccount(double initialBalance) 11: { 12: // costruttore della superclasse 13: super(initialBalance); 14: 15: // inizializza i campi locali 16: transactionCount = 0; 17: } 18: Continua…

70 File CheckingAccount.java 19: public void deposit(double amount) 20: { 21: transactionCount++; 22: // deposita invocando il metodo della superclasse 23: super.deposit(amount); 24: } 25: 26: public void withdraw(double amount) 27: { 28: transactionCount++; 29: // preleva invocando il metodo della superclasse 30: super.withdraw(amount); 31: } 32: 33: /** 34: Deduce le commissioni accumulate e riazzera il 35: contatore delle transazioni. 36: */ Continua…

71 File CheckingAccount.java 37: public void deductFees() 38: { 39: if (transactionCount > FREE_TRANSACTIONS) 40: { 41: double fees = TRANSACTION_FEE * 42: (transactionCount - FREE_TRANSACTIONS); 43: super.withdraw(fees); 44: } 45: transactionCount = 0; 46: } 47: 48: private int transactionCount; 49: 50: private static final int FREE_TRANSACTIONS = 3; 51: private static final double TRANSACTION_FEE = 2.0; 52: }

72 File SavingsAccount.java 01: /** 02: Un libretto bancario con interessi fissi. 03: */ 04: public class SavingsAccount extends BankAccount 05: { 06: /** 07: Costruisce un libretto con un tasso di interesse. 08: @param rate il tasso di interesse 09: */ 10: public SavingsAccount(double rate) 11: { 12: interestRate = rate; 13: } 14: 15: /** 16: Aggiunge gli interessi maturati al conto=. 17: */ Continua…

73 File SavingsAccount.java 18: public void addInterest() 19: { 20: double interest = getBalance() * interestRate / 100; 21: deposit(interest); 22: } 23: 24: private double interestRate; 25: }

74 File AccountTester.java 01: /** 02: Test per la classe BankAccount e le sue sottoclassi 03: 04: */ 05: public class AccountTester 06: { 07: public static void main(String[] args) 08: { 09: SavingsAccount momsSavings 10: = new SavingsAccount(0.5); 11: 12: CheckingAccount harrysChecking 13: = new CheckingAccount(100); 14: 15: momsSavings.deposit(10000); 16: Continua…

75 File AccountTester.java 17: momsSavings.transfer(2000, harrysChecking); 18: harrysChecking.withdraw(1500); 19: harrysChecking.withdraw(80); 20: 21: momsSavings.transfer(1000, harrysChecking); 22: harrysChecking.withdraw(400); 23: 24: // test per le operazioni di fine mese 25: momsSavings.addInterest(); 26: harrysChecking.deductFees(); 27: 28: System.out.println("Mom's savings balance = $ 29: + momsSavings.getBalance()); 30: 31: System.out.println("Harry's checking balance = $ 32: + harrysChecking.getBalance()); 33: } 34: }

76 Invocazione di metodi Due fasi nella selezione del metodo da invocare: Fase statica: seleziona il tipo del metodo, in funzione del tipo statico degli argomenti presenti nel messaggio risoluzione delloverloading Fase dinamica: dispatch seleziona il corpo del metodo, in funzione del tipo dinamico del destinatario dellinvocazione

77 Selezione statica exp.m(a1,..., an) Due fasi: 1.Determina il tipo di exp 2.Seleziona la firma T m(T1,…Tn) del metodo da invocare in base al tipo degli argomenti a1,...,an In questa fase (statica) tipo = tipo statico

78 Selezione Statica / destinatario exp.m(a1,..., an) Determina il tipo statico S di exp : 1.exp = super: S è la superclasse della classe in cui linvocazione occorre: 2.exp = this : S è la classe in cui linvocazione occorre 3.in tutti gli altri casi: S è il tipo dichiarato per exp

79 Selezione statica / metodo exp.m(a1,..., an) exp:S Determina la firma del metodo da invocare 1.calcola il tipo degli argomenti, a1:S1,.... an:Sn 2.determina il best match m(T1,...,Tn) per linvocazione m(a1:S1,… an:Sn), a partire da S Risoluzione overloading se trovi una sola firma: ok altrimenti se nessuno / più di best match: errore

80 Esempio class A { public void m(double d){System.out.println("A.m(double)"); public void m(int i) { System.out.println(A.m(int)"); } } class B extends A { public void m(double d){System.out.print(B.m(double)); } } class over { public static void main(String[] args) { { A a = new B(); a.m(1.5); } } // Selezione statica: BEST(A, m(double)) = { A.m(double) } // Dispatch: B ridefinisce m(double). Quindi esegui B.m(double)

81 Esempio class A { public void m(double d){ System.out.println("A.m(double)"); public void m(int i) { System.out.println(A.m(int)"); } } class B extends A { public void m(double d){System.out.print(B.m(double)); } } class over { public static void main(String[] args) { { A a = new B(); a.m(1); } } // Selezione statica: BEST(A, m(int)) = { A.m(int) } // Dispatch: B non ridefinisce m(int). Quindi esegui A.m(int)

82 Esempio class A { public void m(double d){ System.out.println("A.m(double)"); public void m(int i) { System.out.println(A.m(int)"); } } class B extends A { public void m(double d){System.out.print(B.m(double)); } } class over { public static void main(String[] args) { { B b = new B(); b.m(1); } } // Selezione statica: BEST(B, m(int)) = { A.m(int) } // Dispatch: B non ridefinisce m(int). Quindi esegui A.m(int)

83 Esempio APP(A, m(int)) = { A.m(int) } APP(B, m(String)) = { B.m(String) } APP(A, m(String)) = { } class A { public void m(int i) { System.out.println("A.m(int)"); } } class B extends A { public void m(String s) { System.out.println("B.m(String)"); } } class over { public static void main(String[] args) { B b = new B(); A a = new B(); a.m(1) b.m(a string) ; a = b; a.m(a string); } // BEST(A,m(int)) = {A.m(int)} // BEST(B,m(String))= {B.m(String)} // BEST(A,m(string))= {}

84 Esempio class A { public void m(int i, float f) { /* just return */} public void m(float f, int i) { /* just return */} } class test { public static void main(String[] args) { A a = new A(); a.m(1, 1); } // BEST(A,m(int,int)) = { A.m(int,float), A.m(float,int) }

85 Ereditarietà (ultima)

86 Classi astratte Un ibrido tra classi e interfacce Hanno alcuni metodi implementati normalmente, altri astratti Un metodo astratto non ha implementazione Il metodo, e la classe sono abstract abstract class AccountTransaction {... public abstract void esegui();... }

87 Classi astratte Le classi astratte possono comunque avere costruttori invocabili solo dai costruttori di sottoclasse

88 Classi astratte abstract class AccountTransaction { private BankAccount acct; public AccountTransaction(BankAccount a) { acct = a ; } public BankAccount getAcct() { return acct; } public abstract void esegui(); }

89 Sottoclassi concrete class Prelievo extends AccountTransaction { private double importo; public Prelievo(BankAccount a, double i) { super(a); importo = i; } public void esegui() { getAcct().preleva(i); }

90 Sottoclassi concrete Le classi che estendono una classe astratta DEVONO implementare (sovrascrivere) tutti I metodi astratti altrimenti anche le sottoclassi della classe astratta sono astratte

91 Classi astratte E possibile dichiarare astratta una classe priva di metodi astratti In questo modo evitiamo che possano essere costruiti oggetti di quella classe

92 Eventi del mouse Continua… abstract class MouseAdapter implements MouseListener { // un metodo per ciascun mouse event su una componente public void mousePressed(MouseEvent event) {/* donothing */}; public void mouseReleased(MouseEvent event){/* donothing */}; public void mouseClicked(MouseEvent event) {/* donothing */}; public void mouseEntered(MouseEvent event) {/* donothing */}; public void mouseExited(MouseEvent event) {/* donothing */}; }

93 Eventi del mouse MouseAdapter è abstract e quindi non può essere istanziata direttamente, anche se tutti i suoi metodi sono concreti possiamo estenderla con un classe concreta class MyAdapter extends MouseAdapter { /* gestisce solo gli eventi click */ public void MouseClicked {... } }

94 Metodi e (sotto) classi final È possibile impedire la ridefinizione di un metodo in una sottoclasse, dichiarando il metodo final Oppure dichiarare non ridefinibili tutti i metodi della classe, definendo la classe stessa final Continua…

95 Metodi e (sotto) classi final Due motivazioni Efficienza: metodi final hanno dispatch statico Sicurezza public class SecureAccount extends BankAccout { public final boolean checkPassword(String pwd) {... } } Continua…

96 Controllo degli accessi Java ha quattro livelli di accessibilità per gli elementi di una classe public accessibile dai metodi di qualunque classe private accessibile solo dai metodi della proria classe protected accessibile alle sottoclassi package accessibile da tutte le classi dello stesso package Il livello default, quando non specifichiamo alcun livello di accesso in modo eseplicito

97 Protected abstract class AccountTransaction { private BankAccount acct; protected AccountTransaction(BankAccount a) { acct = a ; } protected BankAccount getAcct() { return acct; } public abstract void esegui(); }

98 Object: la superclasse cosmica Tutte le classi definite senza una clausola extends esplicita estendono automaticamente la classe Object

99 Object: la superclasse cosmica I metodi più utili definiti da questa classe String toString() boolean equals(Object otherObject) Object clone() Spesso overridden nelle classi di sistema e/o nelle classi definite da utente

100 toString() Object.toString() restituisce una stringa ottenuta dalla concatenazione del nome della classe seguita dal codice hash delloggetto su cui viene invocato. Continua…

101 toString() invocato automaticamente tutte le volte che concateniamo un oggetto con una stringa ridefinito in tutte le classi predefinite per fornire una rappresentazione degli oggetti come stringhe Continua… Rectangle box = new Rectangle(5, 10, 20, 30); String s = box.toString(); // s = "java.awt.Rectangle[x=5,y=10,width=20,height=30]"

102 toString() Possiamo ottenere lo stesso effetto nelle classi user-defined, ridefinendo il metodo: public String toString() { return "BankAccount[balance=" + balance + "]"; } new BankAccount(5000).toString(); // restituisce "BankAccount[balance=5000]"

103 equals() nella classe Object coincide con il test == verifica se due riferimenti sono identici sovrascritto in tutte le classi predefinite per implementare un test di uguaglianza di stato Il medesimo effetto si ottiene per le classi user-defined, sempre mediante overriding La firma del metodo in Object : Continua… public boolean equals(Object otherObject)

104 Esempio class Coin { private double value; private String name,... } class CollectibleCoin extends Coin { private int year;... } Monete e monete da collezione

105 Monete uguali public class Coin { // sovrascrive il metodo di object public boolean equals(Object otherObject) { if (otherObject == null) return false; Coin other = (Coin) otherObject; return name.equals(other.name) && value == other.value; }... } NOTE cast per recuperare informazione sul parametro equals per confrontare campi riferimento Continua …

106 Monete uguali Se otherObject non è un Coin, errore... public class Coin {... public boolean equals(Object otherObject) { if (otherObject == null) return false; Coin other = (Coin) otherObject; return name.equals(other.name) && value == other.value; }... } Continua …

107 Monete uguali public class Coin { // nuova versione del metodo equals public boolean equals(Coin other) { if (other == null) return false; return name.equals(other.name) && value == other.value; } // sovrascrive il metodo equals di object public boolean equals(Object other) { if (other == null) return false; if (other instanceof Coin) return equals((Coin)other); return false; }... } cast forza linvocazione della versione corretta

108 Monete uguali class CollectibleCoin extends Coin { { public boolean equals(CollectibleCoin other) {... } public boolean equals(Object other) { if (other == null) return false; if (other instanceof CollectibleCoin) return equals((CollectibleCoin)other); return false; }... Nelle sottoclassi stessa logica …

109 Monete uguali public boolean equals(CollectibleCoin other) { if (!super.equals(other)) return false; return year == other.year; } … ma attenzione alla struttura ereditata!

110 Domanda public boolean equals(CollectibleCoin other) { if (!super.equals(other)) return false; return year == other.year; } private int year; } Quale versione di equals nella classe coin invoca questa chiamata?

111 Risposta La versione con il parametro di tipo Coin other:CollectibleCoin è anche un Coin

112 Domanda Cosa dobbiamo aspettarci dalla chiamata x.equals(x) ? Deve sempre restituire true ?

113 Risposta Si, a meno che, ovviamente, x non sia il riferimento null.

114 clone() Come ben sappiamo, lassegnamento tra due riferimenti crea due riferimenti che puntano allo stesso oggetto Continua… BankAccount account2 = account;

115 clone() Talvolta è utile/necessario creare una copia dellintero oggetto, non del solo riferimento

116 Object.clone() Crea shallow copies (copie superficiali) Continua…

117 Object.clone() Non ripete il cloning sistematicamente sui campi di tipo riferimento Dichiarato protected per proibirne linvocazioni su oggetti la cui classe non abbia ridefinito esplicitamente clone() con livello di accesso public Controlla che loggetto da clonare implementi linterfaccia Cloneable La ridefinizione del metodo nelle sottoclassi richiede attenzione (vedi API)

118 clone() – overriding Una classe che ridefinisce clone() deve attenersi alla firma del metodo nella classe Object Luso del metodo che ne deriva è: Necessario il cast perchè il metodo ha Object come tipo risultato BankAccount cloned = (BankAccount) account.clone(); Object clone()

119

120 Overriding vs overloading Notare che esistono due fasi nella selezione del metodo da invocare: Fase statica: risoluzione delloverloading determina il tipo del metodo, in funzione del tipo statico degli argomenti presenti nel messaggio Fase dinamica: dispatch determina il corpo del metodo, in funzione del tipo dinamico del parametro implicito (ovvero, del destinatario del messaggio)

121 Polimorfismo – run time Dispatch articolato di quanto abbiamo visto … Metodi dispatch dinamico, con le seguenti eccezioni: metodi private, chiamate via super Metodi di classe (static) dispatch è sempre statico Campi dispatch è sempre statico, sia per variabili di istanza, sia per variabili di classe (o static) Continua…

122 Invocazione di metodi exp.m(a1,..., an) per definire precisamente leffetto della chiamata dobbiamo analizzare tre aspetti: selezione statica decide se è corretto invocare m(…) su exp, ovvero se esiste un metodo da invocare determina il tipo del metodo da invocare dispatch determina il corpo del metodo da invocare

123 Selezione statica exp.m(a1,..., an) Due fasi: 1.Determina il tipo di exp 2.Determina la firma T m(T1,…Tn) del metodo da invocare in base al tipo degli argomenti a1,...,an In questa fase (statica) tipo = tipo statico

124 Selezione Statica – Fase 1 exp.m(a1,..., an) Determina il tipo statico S di exp : 1.exp = super: S è la superclasse della classe in cui linvocazione occorre: 2.exp = this : S è la classe in cui linvocazione occorre 3.in tutti gli altri casi: S è il tipo dichiarato per exp

125 Selezione statica – Fase 2 exp.m(a1,..., an) exp:S Determina la firma del metodo da invocare 1.calcola il tipo degli argomenti, a1:S1,.... an:Sn 2.seleziona in S il metodo T m(T1,....,Tn) tale che Si <:Ti e m() è accessibile dal contesto di chiamata 3.se S non ha un metodo m() con le caratteristiche desiderate, ripeti il passo 2 sul supertipo di S (ognuno dei supertipi di S ), finché non trovi un metodo oppure esaurisci la gerarchia.

126 Selezione statica e overloading Lalgoritmo appena visto assume che ogni classe contenga al più una versione del metodo da invocare Che succede se esiste una classe contiene più di una versione? Che succede se una classe ed una superclasse contengono diverse versioni dello stesso metodo? Sono entrambi casi di overloading, e la selezione statica deve risolverlo Selezione statica richiede overloading resolution

127 Selezione statica – Fase 2 rivista exp.m(a1,..., an) exp:S Determina la firma del metodo da invocare 1.calcola il tipo degli argomenti, a1:S1,.... an:Sn 2.determina il best match m(T1,...,Tn) per linvocazione m(a1:S1,… an:Sn), a partire da S Se trovi una sola firma ok, altrimenti errore

128 Esempio class A { public void m(double d){System.out.println("A.m(double)"); public void m(int i) { System.out.println(A.m(int)"); } } class B extends A { public void m(double d){System.out.print(B.m(double)); } } class over { public static void main(String[] args) { { A a = new B(); a.m(1.5); } } // Selezione statica: BEST(A, m(double)) = { A.m(double) } // Dispatch: B ridefinisce m(double). Quindi esegui B.m(double)

129 Esempio class A { public void m(double d){ System.out.println("A.m(double)"); public void m(int i) { System.out.println(A.m(int)"); } } class B extends A { public void m(double d){System.out.print(B.m(double)); } } class over { public static void main(String[] args) { { A a = new B(); a.m(1); } } // Selezione statica: BEST(A, m(int)) = { A.m(int) } // Dispatch: B non ridefinisce m(int). Quindi esegui A.m(int)

130 Esempio class A { public void m(double d){ System.out.println("A.m(double)"); public void m(int i) { System.out.println(A.m(int)"); } } class B extends A { public void m(double d){System.out.print(B.m(double)); } } class over { public static void main(String[] args) { { B b = new B(); b.m(1); } } // Selezione statica: BEST(A, m(int))={A.m(int),B.m(double)}

131 Metodi private : dispatch statico Essendo private, non sono accessibili alle sottoclassi. Quindi le sottoclassi non possono fare overriding di questi metodi Dispatch può essere deciso dal compilatore Continua…

132 Best Match exp.m(a1:S1,..., an:Sn) exp:S 1. Determina linsieme dei metodi applicabili APP(S, m(S1,..., Sn)) = {U1.m(T11,...,T1n),...,Uk.m(Tk1,...,Tkn)} tali che, per ogni j in [1..k] S <: Uj (quindi: esamina S ed i supertipi di S ) m(Tj1, …. Tjn) è definito in Uj ed è visibile nel punto della chiamata exp.m(a1,…,an). Si <: Tji per ogni i in [1..n] 2. Se linsieme è vuoto fallisci

133 3.Altrimenti, calcola linsieme dei metodi migliori BEST(S,m(a1:S1,…,. an:Sn)) rimuovi da APP(S, m(a1:S1,... an:Sn)) ogni Up.m(Tp1,..., Tpn) tale che esiste un metodo migliore, ovvero un metodo Uq.m(Tq1,...,Tqn) tale che - Uq <:Up (è definito in una superclasse più vicina a S ) - Tqi <: Tpi (ha tipi degli argomenti piu vicini agli Si ) 4.Se BEST(S,m(a1:S1,…,an:Sn)) contiene più di un metodo, fallisci. Altrimenti lunico metodo nellinsieme e` il best match per la chiamata exp.m(a1,...,an) Best Match

134 APP(A, m(int)) = { A.m(int) } APP(B, m(String)) = { B.m(String) } APP(B, m(int)) = { A.m(int) } class A { public void m(int i) { System.out.println("A.m(int)"); } } class B extends A { public void m(String s) { System.out.println("B.m(String)"); } } class over { public static void main(String[] args) { B b = new B(); A a = new B(); a.m(1) b.m(a string) ; b.m(1); } Best Match – Esempi // APP(A,m(int)) = {A.m(int)} // APP(B,m(String))= {B.m(String)} // APP(B,m(int)) = {A.m(int)}

135 Best Match – Esempi APP(A, m(int)) = { A.m(int) } APP(B, m(String)) = { B.m(String) } APP(A, m(String)) = { } class A { public void m(int i) { System.out.println("A.m(int)"); } } class B extends A { public void m(String s) { System.out.println("B.m(String)"); } } class over { public static void main(String[] args) { B b = new B(); A a = new B(); a.m(1) b.m(a string) ; a = b; a.m(a string); } // APP(A,m(int)) = {A.m(int)} // APP(B,m(String))= {B.m(String)} // APP(A,m(string))= {}

136 Best Match – Esempi class A { public void m(int i) { System.out.println("A.m(int)"); } } class B extends A { public void m(double f) { System.out.println("B.m"); } } class over { public static void main(String[] args) { B b = new B(); A a = new B(); a.m(1); b.m(1.5); b.m(1); } // APP(A,m(int)) = {A.m(int)} // APP(B,m(double))= {B.m(double)} // APP(B,m(int)) = {A.m(int),B.m(double)} // BEST(B.m(int)) = {A.m(int),B.m(double)}

137 Best Match – Esempi class A { public void m(double g) { System.out.println("A.m"); } } class B extends A { public void m(int i) { System.out.println("B.m"); } } class over { public static void main(String[] args) { B b = new B(); A a = new B(); a.m(1); b.m(1.5); b.m(1); } // APP(A,m(int)) = {A.m(double)} // APP(B,m(double))= {A.m(double)} // APP(B,m(int)) = {A.m(double),B.m(int)} // BEST(B.m(int)) = {B.m(int)}

138 Best Match – Esempi class A { public void m(int i, float f) { /* just return */} public void m(float f, int i) { /* just return */} } class test { public static void main(String[] args) { A a = new A(); a.m(1, 1); } // APP(A, m(int,int)) = { A.m(int,float), A.m(float,int) } // BEST(A,m(int,int)) = { A.m(int,float), A.m(float,int) }

139 Metodi private : dispatch statico class A { public String test() { return this.sd() + ", " + this.dd(); } private String sd(){ return "A.sd()"; } public String dd() { return "A.dd()"; } } class B extends A { public String sd() { return "B.sd()"; } public String dd() { return "B.dd()"; } }... // new B().test() = A.sd(), B.dd() // new A().test() = A.sd(), A.dd() dispatch statico: A.sd() dispatch dinamico: risolto a run time

140 Chiamate via super : dispatch statico class A { public String test() { return dd(); } public String dd() { return "A.dd()"; } } class B extends A { public String dd(){ return (super.dd() + ", " + "B.dd()"); } }... // super.dd() invoca sempre A.dd() // new B().test() = A.dd(), B.dd() // new A().test() = A.dd()

141 Campi: dispatch statico class C { String str = "C"; public void m() { System.out.println(str); } } class D extends C { String str = "D"; public void n() { System.out.println(str); } }... D d = new D(); d.m(); // C d.n(); // D System.out.println(d.str); // D C c = d; c.m();// C ((D)c).n(); // D System.out.println(c.str); // C


Scaricare ppt "Ereditarietà. Concetti principali Ereditarietà e (overriding) di metodi Dynamic dispatch e polimorfismo Ereditarietà e costruttori Livelli di accesso."

Presentazioni simili


Annunci Google