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

Slides:



Advertisements
Presentazioni simili
Training On Line - CONP. 2 Richiesta Da Menu: Conferimenti ad inizio anno termico > Agosto > Pluriennali > Nuova Richiesta Si accede alla pagina di Richiesta.
Advertisements

Programmazione ad oggetti
Dipartimento di Ingegneria Idraulica e Ambientale - Universita di Pavia 1 Caduta non guidata di un corpo rettangolare in un serbatoio Velocità e rotazione.
Classi ed Oggetti in JAVA
1 Pregnana Milanese Assessorato alle Risorse Economiche Bilancio Preventivo P R O P O S T A.
© 2007 SEI-Società Editrice Internazionale, Apogeo Unit à B2 Gli oggetti: concetti avanzati.
Recupero debito quarto anno Primo incontro
Recupero debito quarto anno Secondo incontro
Informatica Recupero debito quarto anno Terzo incontro.
Informatica 2 Lezione 4 Corso di laurea in matematica Informatica 2 Dott. Ing. Leonardo Vito Corso di laurea matematica indirizzo matematica per le applicazioni.
Differenze nei vari linguaggi di Elisa Trifirò e Barbara Tacchino
1 Astrazioni sui dati : Specifica ed Implementazione di Tipi di Dato Astratti in Java.
1 Semantica Operazionale di un frammento di Java: lo stato.
Classi ed Oggetti in Java (Cenni). Richiami Ruolo delle Classi in Java Oggetti.
Le gerarchie di tipi.
LIP: 19 Aprile Contenuto Soluzione Compitino Tipo di dato MultiSet, estensione con sottoclasse.
Liste Ordinate 3 Maggio Ultima Lezione Abbiamo visto i tipi di dato astratti IntList e StringList Realizzano liste di interi e di stringhe Realizzati.
LIP: 1 Marzo 2005 Classe Object e Vettori. Partiamo da Lesercizio dellultima esercitazione realizzato tramite array Vedremo come si puo fare in modo piu.
Frontespizio Economia Monetaria Anno Accademico
9/12/2004Laboratorio di Programmazione - Luca Tesei1 Ereditarietà
Dispatch.
Interfacce. Interfacce come strumento di progetto Scelta delle classi di un progetto Criteri di coesione e accoppiamento Interfacce e riuso di codice.
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE Puntatori Marco D. Santambrogio – Ver. aggiornata al 21 Marzo 2013.
1 Programmazione ad oggetti in Java E.Mumolo, DEEI
Canale A. Prof.Ciapetti AA2003/04
1 Lezione XIII Lu 17-Nov-2005 Programmare le classi.
Derivazione tra classi
Istruzioni di selezione in Java Programmazione Corso di laurea in Informatica.
Approfondimento delle classi
1 Le gerarchie di tipi. 2 Supertipi e sottotipi 4 un supertipo –class –interface 4 può avere più sottotipi –un sottotipo extends il supertipo ( class.
Ereditarietà. Concetti principali Ereditarietà e (overriding) di metodi Dynamic dispatch e polimorfismo Ereditarietà e costruttori Livelli di accesso.
Subtype Polymorphism. Interfacce e subtype polimorfismo Tipi, sottotipi e conversioni di tipo Polimorfismo e dinamic dispatch.
Progetto di applicazioni grafiche. Disegno di forme complesse Prassi : un classe per ciascuna forma Progetta la forma individuando le componenti base.
Classi. Progetto di classi: Dalla specifica dellinterfaccia, alla definizione dellimplementazione Metodi Costruttori Documentazione e commenti Variabili.
Programmazione con Interfacce
Capitolo 9 Interfacce e polimorfismo
Capitolo 10 Ereditarietà
Capitolo 3 Realizzare classi
Java base IV: Java e la programmazione O.O.
IL TEMA DELLA RIUSABILITÀ Si vuole riusare tutto ciò che può essere riusato (componenti, codice, astrazioni) Non è utile né opportuno modificare codice.
IL TEMA DELLA RIUSABILITÀ Si vuole riusare tutto ciò che può essere riusato (componenti, codice, astrazioni) Non è utile né opportuno modificare codice.
Programmazione in Java (8)
AlgoLab - Ereditarieta' Ereditarietà e polimorfismo in Java Laboratorio di Algoritmi 02/03 Prof. Ugo de Liguoro.
1 Negozi Nuove idee realizzate per. 2 Negozi 3 4.
ISTITUTO STATALE DI ISTRUZIONE SUPERIORE F. ENRIQUES CORSO JAVA – PROVA INTERMEDIA DEL 12 MARZO 2007 NOME: COGNOME: ________________________________________________________________________________.
Enumerazioni e Classi 1. Enumerazioni Permettono di definire nuovi tipi che consistono in un insieme di valori costanti (ognuno con un nome) – Migliorano.
Fopndamenti di programmazione. 2 La classe String Una stringa è una sequenza di caratteri La classe String è utilizzata per memorizzare caratteri La classe.
Fondamenti di Programmazione Prof.ssa Elisa Tiezzi
Sviluppare un programma in C che, dato un array da 100 elementi interi caricato con numeri casuali compresi tra [10,100], sia in grado di cercare il valore.
ISTITUTO STATALE DI ISTRUZIONE SUPERIORE F. ENRIQUES CORSO JAVA – PROVA INTERMEDIA DEL 12 MARZO 2007 NOME: COGNOME: ________________________________________________________________________________.
I nomi in Java F. Bombi 18 novembre novembre 2003.
30/11/2004Laboratorio di Programmazione - Luca Tesei1 Interfacce e Polimorfismo.
Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.
1 Osservazioni Generali Struttura di programma Gerarchia di classi: overloading, overriding, e dispatching Analisi ed esecuzione Modificabilità e condivisione.
Fondamenti di informatica Oggetti e Java Luca Cabibbo Luca Cabibbo – Fondamenti di informatica: Oggetti e Java Copyright © 2004 – The McGraw-Hill Companies.
Ripasso su Java. Introduzione Per risolvere problemi complessi, i linguaggi di programmazione forniscono costrutti per realizzare nuove funzioni che trasformino.
Programmazione in Java. Classi I programmi in Java consistono di classi. Le classi consentono di definire: collezioni di procedure (metodi statici) tipi.
Esercitazione del 7 marzo 2008 Ereditarieta’. Esercizio: soluzione Implementare la seguente specifica che definisce un tipo di dato Libro.
Cose nuove di Java (prima a chiacchiera, poi formalmente)
LIP: 2 Maggio 2008 Classi Astratte. Cos’e’ una Classe Astratta una classe astratta e’ un particolare tipo di classe permette di fornire una implementazione.
Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.
Ereditarieta’. Contenuti Introduciamo un meccanismo fondamentale di Java: l’ereditarieta’ Permette di estendere classi gia’ definite (ovvero di definire.
Progettare una classe 21 Febbraio La classe BankAccount Vogliamo realizzare una classe i cui oggetti sono dei semplici conti bancari. * Identifichiamo.
Classi ed Oggetti in Java (Cenni). Richiami Cenni sull’ Implementazione, Macchine Astratte, Compilatore, Interprete Ruolo delle Classi in Java Oggetti.
Esercitazione del 9 marzo 2007 Ereditarieta’. Richiami Definire sottoclassi (ereditarieta’) Overriding Specificatori di accesso (private, protected) Principio.
LIP: 4 Maggio 2007 Interfacce. Cos’e’ una Interfaccia una interfaccia e’ un particolare tipo di classe contiene solo la specifica non ha implementazione.
Corso di Algoritmi e Strutture Dati con Laboratorio Richiami di Java – parte II.
Introduzione all’Ereditarietà Pietro Palladino. Richiami UML Classe: descrizione di un insieme di oggetti software con caratteristiche simili Definisce.
Transcript della presentazione:

Ereditarietà

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

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 }

Ereditarietà – introduzione SavingsAccoun t eredita automaticamente tutti i 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

Superclassi vs interfacce Estendere una classe è diverso da implementare una interfaccia Elementi comuni: entrambi i meccanismi generano sottotipi Istanze di una sottoclasse possono essere assegnate a variabili della superclasse Istanze di una classe possono essere asseganate a variabili dellinterfaccia che la classe implementa Continua…

Superclassi vs interfacce Estendere una classe è diverso da implementare una interfaccia Elementi di differenza: Estendendo una classe ereditiamo anche il comportamento (limplementazione dei metodi ed i campi) della superclasse riuso di codice È possibile estendere al più una classe (in Java) mentre si possono implementare più interfacce

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

Ereditarietà – introduzione Nellimplementazione di una sottoclasse definiamo solo i campi aggiuntivi, ed i metodi aggiuntivi o modificati ( overridden ) public class SavingsAccount extends BankAccount { public SavingsAccount(double rate) { interestRate = rate; } public void addInterest() { double interest = getBalance() * interestRate / 100; deposit(interest); } private double interestRate; } In questo caso solo campi e metodi nuovi

Ereditarietà – introduzione Encapsulation : la sottoclasse non ha accesso ai campi privati della superclasse addInterest() invoca getBalance() e deposit() per modificare il saldo il campo balance non può essere riferito essendo private ) Nota: addInterest() invoca getBalance() senza specificare un oggetto linvocazione riguarda this, listanza della sottoclasse che eredita anche la struttura della superclasse

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

Sintassi: estensione di classe class SubclassName extends SuperclassName { metodi campi } Continua…

Sintassi: estensione di classe Esempio: public class SavingsAccount extends BankAccount { public SavingsAccount(double rate) { interestRate = rate; } public void addInterest() { double interest = getBalance() * interestRate / 100; deposit(interest); } private double interestRate; } Scopo: Definire una nuova classe che eredita da una classe esistente e definisce nuovi metodi e campi.

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

Risposta deposit, withdraw, getBalance, e addInterest.

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

Risposta Manager è la sottoclasse; Employee la superclasse

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

Gerarchie di classi – Swing Continua…

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

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

Risposta Rappresenta gli aspetti comuni del comportamento delle classi JTextField e JTextArea

Una gerarchia di conti bancari Continua… Progettiamo una applicazione per una banca che voglia offrire ai suoi clienti due tipi di conti bancari: Checking account: non offre interessi, ma permette un numero di transazioni gratuite e per le altre commissioni basse Savings Account: interessi calcolati su base mensile e transazioni con commissione

Una gerarchia di conti bancari Continua…

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

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 ( overloading ) Continua…

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…

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

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 Deve anche soddisfare lo stesso contratto (vedremo …) Continua…

Ereditarietà – overriding di metodi Invocazione basata su dispatch dinamico Se diretto ad un oggetto della sottoclasse, un messaggio invoca sempre il metodo della sottoclasse (come ci si aspetta) Continua…

Domanda Date le due dichiarazioni Quali dei seguenti comandi compilano correttamente? Per quelli che compilano quale è leffetto dellesecuzione? Continued… BankAccount b1 = c; BankAccount b2 = s; b1.deductFees(); c.deductFees(); s.deductFees(); ((CheckingAccount)b1).deductFees(); b1 = b2; ((CheckingAccount)b1).deductFees(); CheckingAccount c = new CheckingAccount(10); SavingsAccount s = new SavingsAccount();

Risposta CheckingAccount c = new CheckingAccount(10); SavingsAccount s = new SavingsAccount(); BankAccount b1 = c; // compila, esegue lassegnamento BankAccount b2 = s; // compila, esegue lassegnamento b1.deductFees(); // errore di compilazione c.deductFees(); // compila, invoca il metodo s.deductFees(); // errore di compilazione // compila, esegue il metodo ((CheckingAccount)b1).deductFees(); b1 = b2; // compila, esegue lassegnamento // compila ma ClassCastException in esecuzione ((CheckingAccount)b1).deductFees();

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

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)

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 }

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

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

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

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…

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

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! }

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

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); }... }

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! }... }

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); }

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

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

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

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() ?

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.

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…

Costruttori di sottoclasse super seguito da una parentesi indica una chiamata al costruttore di superclasse NB: non confondere con la chiamata di metodo della superclasse super.m(…) Continua… public class CheckingAccount extends BankAccount { public CheckingAccount(double initialBalance) { // Construct superclass super(initialBalance); // Initialize transaction count transactionCount = 0; }... }

Costruttori di sottoclasse La chiamata a super(…) deve essere il primo comando del costruttore 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

Sintassi ClassName(parameters) { super(parameters);... } Esempio: public CheckingAccount(double initialBalance) { super(initialBalance); transactionCount = 0; } Scopo: Invocare il costruttore di superclasse. Deve essere il primo comando del costruttore della sottoclasse.

Domanda Assumendo che questa implementazione di SavingsAccount compili correttamente, cosa possiamo dire riguardo i costruttori della classe BankAccount ? public class SavingsAccount extends BankAccount { public SavingsAccount(double rate) { interestRate = rate; }... }

Risposta Che BankAccount ha un costruttore senza parametri

Conversioni di tipo e subtyping Una sottoclasse è un sottotipo della sua superclasse Quindi, riferimenti di tipo sottoclasse possono essere trattati come riferimenti di tipo superclasse SavingsAccount collegeFund = new SavingsAccount(10); BankAccount anAccount = collegeFund; Object anObject = collegeFund;

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

Conversioni di tipo e subtyping Utilizzare un riferimento di tipo superclasse causa una perdita di informazione Assegnando un riferimento di sottoclasse ad una variabile di superclasse: il valore del riferimento assegnato alla variabile punta correttamente allo stesso oggetto Ma il tipo della variabile rivela meno informazione sulloggetto Continua… // anAccount : BankAccount anAccount.deposit(1000); // OK anAccount.addInterest(); // NO

Conversioni di tipo e subtyping Perchè convertire, quindi? Per riutilizzare codice scritto in termini del supertipo, (e che potrebbe anche non conoscere il sottotipo) : Questo codice può essere utilizzato per trasferimenti su qualunque BankAccount public void transfer(double amount, BankAccount other) { withdraw(amount); other.deposit(amount); }

Conversioni di tipo e downcasting In alcune situazioni è utile/necessaria la conversione inversa: da supertipo a sottotipo Utilizziamo cast … Questo però può essere pericoloso: se anotherAccount non punta ad un SavingsAccount genera un errore (eccezione) Continua… SavingsAccout anAccount = (SavingsAccount) anotherAccount.

Conversioni di tipo e downcasting Soluzione: utilizzo delloperatore instanceof verifica se anObject punta ad un oggetto di tipo BankAccount (o di un sottotipo) if (anObject instanceof BankAccount) { BankAccount anAccount = (BankAccount) anObject;... }

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

Domande Nel metodo transfer() qui di seguito, che succede se modifichiamo il tipo del secondo parametro da BankAccount a SavingsAccount ? da BankAccount a Object ? public void transfer(double amount, BankAccount other) { withdraw(amount); other.deposit(amount); }

Risposte Nel primo caso perdiamo la possibilità di utilizzare il metodo su oggetti di tipo BankAccount e di tipo CheckingAccount Nel secondo caso abbiamo un errore perchè il metodo deposit() non può essere invocato su una variabile di tipo Object

Polimorfismo – dynamic dispatch Il tipo di una variabile non determina in modo univoco il tipo delloggetto che la variabile riferisce Meccanismo già discusso per le interfacce Ogni variabile ha due tipi Statico: il tipo dichiarato Dinamico: il tipo delloggetto a cui la variabile riferisce Continua… BankAccount aBankAccount = new SavingsAccount(1000); // tipo statico: BankAccount // tipo dinamico: SavingsAccount

Polimorfismo – dynamic dispatch Il metodo invocato da un messaggio è determinato dal tipo dinamico della variabile, non dal tipo statico Continua… BankAccount anAccount = new CheckingAccount(); anAccount.deposit(1000); //chiama CheckingAccount.deposit()

Polimorfismo – dynamic dispatch Supponiamo di includere il metodo transfer in BankAccount Continua…

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…

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);

Polimorfismo – dynamic dispatch Il compilatore verifica che esista un metodo da selezionare in risposta al messaggio Meglio: verifica lesistenza del metodo, e decide il tipo del metodo da invocare tra le possibili versioni overloaded Il corpo del metodo, con il tipo selezionato, viene determinato a run time. Object anObject = new BankAccount(); anObject.deposit(1000); // Wrong!

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. initialBalance the initial balance 18: */ Continua…

File BankAccount.java 19: public BankAccount(double initialBalance) 20: { 21: balance = initialBalance; 22: } 23: 24: /** 25: Peposita un importo sul conto. 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. amount limporto da prelevare 36: */ Continua…

File BankAccount.java 37: public void withdraw(double amount) 38: { 39: balance = balance - amount; 40: } 41: 42: /** 43: Saldo corrente del conto. 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 amount limporto da trasferire other il conto su cui trasferire 55: */ Continua…

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: }

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. 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…

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…

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: }

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. 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…

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

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…

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: }

Domande Supponiamo che a sia una variabile di tipo BankAccount con valore diverso da null. Cosa possiamo dire riguardo alloggetto riferito da a ? Se a riferisce un CheckingAccount, quale è leffetto del messaggio a.transfer(1000, a) ?

Risposte Loggetto è un instanza della classe BankAccount o di una sottoclasse. Il saldo è invariato, il contatore delle transazioni viene incrementato due volte.

(Super)classi abstract Nella definizione di una sottoclasse possiamo scegliere se ridefinire o meno i metodi della superclasse La interfaccia della superclasse può però rendere la ridefinizione dei metodi necessaria È il caso in cui nella superclasse si vuole fornire un metodo nellinterfaccia, senza darne una implementazione Il metodo, e la superclasse sono abstract

(Super)classi abstract Classi abstract utili per descrivere linterfaccia comune di un insieme di classi definire alcuni aspetti dellimplementazione (struttura e metodi) lasciandone non speficicati altri Un costrutto intermedio tra classi e interfacce

(Super)classi abstract Esempio: potremmo ripensare alla gerarchia radicata in BankAccount, e decidere di strutturarla in modo che tutti i conti debbano avere un metodo deductFees() Simile alla situazione dei metodi deposit() e deposit() Ma per deductFees() non abbiamo una implementazione utile per le sottoclassi Continua…

(Super)classi abstract Due possibilità: Definiamo un metodo con corpo vuoto Alternativa migliore: forziamo le sottoclassi a fornire una implementazione definendo il metodo, e la classe abstract Continua… public abstract class BankAccout {... public abstract void deductFees();... }

(Super)classi abstract Una classe che definisce (o eredita) un metodo abstract deve a sua volta essere dichiarata abstract Una sottoclasse può sovrascrivere un metodo abstract fornendo una implementazione Una classe abstract non può essere istanziata Questo non significa che non possa avere un costruttore Il costruttore sarà invocato dalle sottoclassi che forniscono implementazione ai metodi abstract

Eventi del mouse MouseListener vs MouseAdapter 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 */}; }

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 Continua… class MyAdapter1 extends MouseAdapter { /* inutile, ma compila e può essere istanziata } class MyAdapter2 extends MouseAdapter { /* gestisce solo gli eventi click */ public void MouseClicked {... } }

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…

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…

Controllo degli accessi Java ha quattro livelli di accessibilità per campi, metodi e classi interne. 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

Livelli di accesso raccomandati Variabili di istanza private. Eccezioni: public static per costanti (campi final) Esempio: System.out, accessibile a tutte le classi Campi di classi strettamente interconnesse allinterno di un package considerate se non sia più opportuno utilizzare classi interne Continua…

Livelli di accesso raccomandati Metodi: public, private protected può essere una opzione ragionevole in questo caso Classi e interfacce: public o package Alternativa allaccesso package: classi interne In generale, per le classi interne utilizziamo le stesse prassi dei campi (mai public ) Esistono eccezioni: Ellipse2D.Doubl e Attenzione ai default: omettere public o private implica accesso package

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

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

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

toString() ridefinito in tutte le classi predefinite per fornire una rappresentazione degli oggetti come stringhe invocato automaticamente tutte le volte che concateniamo un oggetto con una stringa 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]"

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

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)

equals() – overriding Sovrascrivendo equals() manteniamo la firma che il metodo ha nella classe Object public class Coin {... public boolean equals(Object otherObject) { Coin other = (Coin) otherObject; return name.equals(other.name) && value == other.value; }... } Notiamo cast per recuperare informazione sul parametro equals per confrontare campi riferimento Continua …

equals() – overriding Se otherObject non è un Coin, errore... Possiamo fare meglio! 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 …

equals() – overriding public class Coin { public boolean equals(Coin other) { return name.equals(other.name) && value == other.value; } public boolean equals(Object other) { if (other == null) return false; if (other instanceof Coin) return equals((Coin)other); return false; }... } overloading overriding cast forza linvocazione della versione overloaded Continua… Dato coin:Coin, consideriamo la chiamata coin.equals(obj)

equals() – overriding public 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; }... Continua… Nelle sottoclassi stessa logica …

equals() – overriding public boolean equals(CollectibleCoin other) { if (!super.equals(other)) return false; return year == other.year; } private int year; } … ma attenzione alla struttura ereditata!

Domanda public boolean equals(CollectibleCoin other) { if (!super.equals(other)) return false; return year == other.year; } private int year; } NB: quale versione di equals nella classe Coin invoca la chiamata super.equals(other) ?

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

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

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

Domanda È possibile implementare equals in termini di toString ? È ragionevole come idea?

Risposta Se toString restituisce una stringa che descrive i campi delloggetto, questa è una implementazione possibile Invoca toString() su this e sul parametro esplicita e confronta le due stringhe Tuttavia, è più efficiente confrontare i campi direttamente piuttosto che convertirli prima in stringhe e poi confrontarli.

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

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

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

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)

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()

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)

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…

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

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

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

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.

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

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 T m(T1,...,Tn) per linvocazione m(a1:S1,… an:Sn), a partire da S Se trovi una sola firma ok, altrimenti errore

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

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

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)}

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))= {}

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)}

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)}

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) }

Invocazione di metodi exp.m(a1,..., an) per definire precisamente leffetto della chiamata dobbiamo analizzare tre aspetti: selezione statica: determina la firma del metodo da invocare calcolo del best match dispatch dinamico: determina il corpo del metodo da invocare se il dispatch è statico esegui il corpo del metodo determinato dalla selezione statica altrimenti esegui il corpo del metodo con il tipo determinato dalla selezione statica che trovi a partire dal tipo dinamico di exp

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)

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)

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)}

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…

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

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()

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