Il Linguaggio Java Ereditarietà Ereditarietà • L'ereditarietà permette di creare nuove classi sulla base di classi esistenti • In particolare, permette di • riusare il codice (metodi e campi); • aggiungere nuovi metodi e nuovi campi; • ridefinire metodi e campi esistenti (overriding) TIGA 2 Parola chiave extends class A { // ... } class B extends A { // Differenze rispetto ad A } Classe A: classe base, classe padre o superclasse Classe B:classe derivata, classe figlio, o sottoclasse TIGA 3 Ereditarietà • La sottoclasse • eredita tutti i membri della superclasse • può aggiungere nuovi membri • può ridefinire i metodi della superclasse • non può accedere ai membri privati della superclasse TIGA 4 Esempio: conti bancari Un SavingsAccount (conto di risparmio) • è un BankAccount (conto bancario), che • frutta un tasso di interesse fisso sui depositi Rispetto a BankAccount, SavingsAccount • ha un campo interestRate che specifica il tasso di interesse • ha un metodo addInterestRate che applica il tasso di interesse • ha un costruttore che imposta il valore iniziale del tasso di interesse TIGA 5 La classe SavingsAccount class SavingsAccount extends BankAccount { public SavingsAccount(double aRate) { // ... } public void addInterest() { // ... } private double interestRate; } TIGA 6 Oggetto di una sottoclasse SavingsAccount balance porzione ereditata da BankAccount interestRate TIGA 7 Accesso ai metodi della superclasse public void addInterest() { double interest = getBalance() *interestRate / 100; deposit(interest); } Le chiamate ai metodi della superclasse (getBalance, deposit) utilizzano il parametro implicito this del metodo addInterest TIGA 8 La classe Object Una classe A che non estende esplicitamente un’altra classe costituisce una sottoclasse della classe Object Object BankAccount Principali metodi di Object • toString • clone • equals SavingsAccount TIGA 9 Conversioni tra tipi riferimento (I) ESTENSIONE DI TIPO RIFERIMENTO S T purché S sia una sottoclasse di T (S è un T) class T {/* ... */} class S extends T {/*…} La conversione da T verso S è una riduzione TIGA 10 Conversioni tra tipi di classi (II) • Assegnamento ed invocazione di metodo: sono consentite solo le conversioni identità e per estensione • Esempio T t = new T(); S s = new S(); t = s; // S è un T TIGA 11 Conversioni tra tipi di classi (II) • Casting consente la conversione identità e le conversioni per estensione e per riduzione • Esempio T t = new T(); S s; s = (S)t; // Si forza t ad essere un S. Pericoloso!! TIGA 12 Conversioni tra tipi di classi (III) SavingsAccount mio = new SavingsAccount(10); BankAccount unConto = mio; // OK Object unOggetto = mio; // OK mio.addInterest(); // OK unConto.deposit(20); // OK unConto.addInterest(); // compile-time error unOggetto.deposit(); // compile-time error • le tre variabili oggetto mio, unConto ed unOggetto riferiscono lo stesso oggetto… • ma unConto non “sa tutto” dell’oggetto mio ed • unOggetto ne sa ancora meno… • ma allora a cosa serve??? TIGA 13 Riuso del codice della superclasse SavingsAccount mio = new SavingsAccount(10); BankAccount tuo = new BankAccount(10); … tuo.transfer(mio, 5.0); al metodo transfer non serve sapere tutto dell’oggetto mio, gli basta sapere che è un BankAccount TIGA 14 Cast tra tipi riferimento SavingsAccount mio = new SavingsAccount(10); … Object unOggetto = mio; // mio è un Object … SavingsAccount unConto = (savingsAccount)unOggetto; … unConto.addInterest(); // OK … Se, però, unOggetto non riferisce un oggetto istanza di SavingsAccount, l’invocazione del metodo produce un errore a run-time TIGA 15 Parola chiave instanceof Conviene sempre verificare la fattibilità di un cast di tipo riferimento if ( unOggetto instanceof SavingsAccount ) unConto = (SavingsAccount)unOggetto; else unConto = null; TIGA 16 Gerarchie di ereditarietà (I) •Il conto corrente (CheckingAccount) è un conto bancario (BankAccount) che • non ha interessi, • offre un numero limitato di operazioni mensili gratuite ed • addebita una commissione per ciascun movimento aggiuntivo •Il libretto di risparmio (SavingsAccount) è un conto bancario che • frutta interessi mensili TIGA 17 Gerarchie di ereditarietà (II) •Il conto vincolato (TimeDepositAccount) è un conto di risparmio (SavingsAccount) che • impegna a lasciare il denaro nel conto per certo numero di mesi e • prevede una penale per un prelievo anticipato TIGA 18 Gerarchie di ereditarietà (III) BankAccount getBalance deposit withdraw CheckingAccount deductFees deposit withdraw SavingsAccount addInterest TimeDepositAccount addInterest withdraw TIGA 19 Metodi nella sottoclasse • La sottoclasse può • ereditare uno o più membri della superclasse • definire nuovi metodi (membri) • sovrascrivere (ridefinire) uno o più metodi della superclasse TIGA 20 Metodi nella sottoclasse (I) • Ereditare un metodo dalla superclasse • Se un metodo della superclasse non viene sovrascritto (ridefinito) allora tale metodo viene ereditato e può essere applicato agli oggetti della sottoclasse • Esempio • La classe CheckingAccount eredita il metodo BankAccount.getBalance dalla superclasse BankAccount TIGA 21 Metodi nella sottoclasse (II) • Definire nuovi metodi nella sottoclasse • Se nella sottoclasse si definisce un metodo che non esiste nella superclasse allora tale metodo può essere applicato solo agli oggetti della sottoclasse • Esempio • Il metodo SavingsAccount.addInterest TIGA 22 Metodi nella sottoclasse (III) • Sovrascrivere (override) un metodo della superclasse • Nella sottoclasse si definisce un metodo m2 con la stessa firma del metodo m1 della superclasse • Il metodo m2 sovrascrive m1, cioè quando si applica il metodo ad un oggetto della sottoclasse viene eseguito m2 • Esempio • CheckingAccount.deposit sovrascrive BankAccount.deposit TIGA 23 La classe CheckingAccount public class CheckingAccount extends BankAccount { public CheckingAccount(int initialBalance) {/* costruttore */} public void deposit(double amount) {/* override */} public void withdraw(double amount) {/* override */} public void deductFees() {/* define */} private int transactionCount; private static final int FREE_TRANSACTIONS = 3; private static final double TRANSACTION_FEE = 3.0; } TIGA 24 La classe CheckingAccount • public void deposit(double amount) accredita amount su questo conto corrente ed incrementa il numero di transazioni eseguite • public void withdraw(double amount) preleva amount da questo conto corrente ed incrementa il numero di transazioni eseguite • public void deductFees() addebita la commissione complessiva ottenuta applicando la commissione TRANSACTION_FEE ad ogni transazione in esubero rispetto a FREE_TRANSACTIONS e reimposta a zero il numero di transazioni eseguite TIGA 25 La parola chiave super public class CheckingAccount extends BankAccount { public void deposit(double amount) { transactionCount++; deposit(amount); // Ricorsione infinita!! } TIGA 26 La parola chiave super public class CheckingAccount extends BankAccount { public void deposit(double amount) { transactionCount++; super.deposit(amount); // OK!! } TIGA 27 La parola chiave super public void deductFees() { if (transactionCount > FREE_TRANSACTIONS) { double fees = TRANSACTION_FEE * (transactionCount – FREE_TRANSACTIONS); super.withdraw(fees); } transactionCount = 0; } TIGA 28 Differenza tra super e this •La parola chiave this • costituisce un riferimento al parametro implicito; • come prima istruzione, permette di invocare un altro costruttore della stessa classe •La parola chiave super • non costituisce un riferimento ad un oggetto • permette di invocare un metodo sovrascritto della superclasse • come prima istruzione, permette di invocare un costruttore della superclasse TIGA 29 Costruzione della sottoclasse •Problema nel costruttore della sottoclasse si vuole impostare il valore iniziale delle variabili ereditate, ma la sottoclasse non ha accesso alle variabili private della superclasse •Soluzione: si invoca il costruttore della superclasse utilizzando la parola chiave super TIGA 30 Costruzione della sottoclasse public CheckingAccount(int initialBalance) { // si costruisce la superclasse super(initialBalance); // si inizializza il contatore delle transazioni transactionCount = 0; } TIGA 31 Variabili istanza nella sottoclasse (I) •Ereditare una variabile istanza della superclasse •Tutte le variabili istanza della superclasse sono ereditate automaticamente •Esempio: balance di BankAccount •Definire nuove variabili istanza nella sottoclasse •Qualunque nuova variabile definita nella sottoclasse esiste solo per gli oggetti istanza della sottoclasse •Esempio: transactionCount di CheckingAccount TIGA 32 Variabili istanza nella sottoclasse •Mettere in ombra una variabile della superclasse • Se nella sottoclasse F si dichiara una variabile istanza v con lo stesso nome N di una variabile istanza w della superclasse P, allora v mette in ombra w, cioè • ogni volta che un metodo della sottoclasse utilizza il nome N si riferirà alla variabile v TIGA 33 Variabili in ombra public class CheckingAccount extends BankAccount { public void deposit(double amount) { transactionCount++; balance += amount; } // … private double balance; } … CheckingAccount harrys = new CheckingAccount(100); … harrys.deposit(1500); System.out.println(harrys.getBalance()); // 100 TIGA 34 Costruzione della superclasse •Chiamata esplicita: La chiamata al costruttore della superclasse mediante super deve la prima istruzione del costruttore della sottoclasse •Chiamata implicita: Se un costruttore della sottoclasse non chiama esplicitamente un costruttore della superclasse, viene chiamato automaticamente il costruttore di default (senza argomenti) della superclasse se la superclasse non ha costruttore di default, il compilatore dà errore TIGA 35 Gerarchie di ereditarietà BankAccount getBalance deposit withdraw CheckingAccount deductFees deposit withdraw SavingsAccount addInterest TimeDepositAccount addInterest withdraw TIGA 36 Ereditarietà indiretta • La classe SavingsAccount è la superclasse diretta della classe TimeDepositAccount • La classe BankAccount è una superclasse indiretta di TimeDepositAccount • TimeDepositAccount eredita i metodi getBalance e deposit da BankAccount Si possono ereditare metodi da una superclasse indiretta a patto che le superclassi intermedie non li ridefiniscano TIGA 37 Caricamento ed inizializzazione •In un linguaggio tradizionale, •il formato eseguibile di un programma viene caricato interamente; •viene eseguita l’inizializzazione della parte statica; •infine, il controllo passa al programma TIGA 38 Caricamento ed inizializzazione •Nel linguaggio Java •la classe (il file .class) viene caricata al momento del suo primo utilizzo (quando è necessario) e cioè •al primo accesso di un membro statico; •oppure quando viene creato il primo oggetto della classe •nel punto di primo utilizzo di una classe avviene l’inizializzazione dei suoi membri static TIGA 39 Polimorfismo (I) •Un oggetto della sottoclasse è anche un oggetto della superclasse, perciò •È sempre possibile usare un oggetto della sottoclasse al posto di uno della superclasse BankAccount mio = new BankAccount(100); CheckingAccount tuo = new CheckingAccount (10); mio.transfer(tuo, 5); Quale metodo deposit viene chiamato? Si vorrebbe CheckingAccount.deposit… TIGA 40 Polimorfismo (II) BankAccount BankAccount balance this other amount CheckingAccount transfer balance transactionCount Variabile Tipo della variabile this other Tipo dell’oggetto BankAccount BankAccount BankAccount CheckingAccount TIGA 41 Polimorfismo (III) • Le chiamate di metodo sono sempre determinate dal tipo dell’oggetto effettivo e non dal tipo della variabile riferimento Perció, • una stessa chiamata (es., tuo.deposit()) può chiamare metodi diversi; • il metodo che viene effettivamente chiamato è determinato a tempo di esecuzione (late binding o dynamic binding) TIGA 42 Chiamata di un metodo Il compilatore risolve x.f( args ), con x di classe X, eseguendo i seguenti passi: • determina i metodi con la stessa firma f ed appartenenti alla classe X ed alle sue superclassi; • seleziona quello più specifico tra quelli accessibili (Se tale metodo non esiste, il compilatore genera un errore) • se il metodo selezionato è private, static o final, oppure se è un costruttore, produce codice per effettuare la chiamata (static/early binding); • altrimenti, produce codice per: (1) determinare il metodo da invocare e (2) effettuare la chiamata (dynamic/late binding) TIGA 43 Dynamic or late binding • JVM seleziona il metodo da chiamare in base al tipo effettivo dell’oggetto. • se l’oggetto è di classe Y, derivata da X, ed Y ha il metodo f, allora JVM invoca tale metodo; • altrimenti, JVM invoca X.f • È inefficiente eseguire questa selezione ad ogni chiamata di metodo. Perciò, per ogni classe viene costruita la Tabella dei metodi TIGA 44 La Tabella dei metodi BankAccount this BankAccount other CheckingAccount TIGA deposit() withdraw() getBalance() transfer() BankAccount.deposit() BankAccount. withdraw () BankAccount.getBalance () BankAccount.transfer() deposit() withdraw() getBalance() transfer() CheckingAccount.deposit() CheckingAccount. withdraw() BankAccount.getBalance () BankAccount.transfer() 45