Ereditarietà e classi astratte



Documenti analoghi
Modulo 4: Ereditarietà, interfacce e clonazione

Programmazione a Oggetti Lezione 10. Ereditarieta

Progettazione : Design Pattern Creazionali

Parola chiave extends

Guida all uso di Java Diagrammi ER

12 - Introduzione alla Programmazione Orientata agli Oggetti (Object Oriented Programming OOP)

Uso di JUnit. Fondamenti di informatica Oggetti e Java. JUnit. Luca Cabibbo. ottobre 2012

Versione 7.0 Taglie e Colori. Negozio Facile

SOMMARIO... 3 INTRODUZIONE...

Gestione degli eventi in Java

Capitolo 3. L applicazione Java Diagrammi ER. 3.1 La finestra iniziale, il menu e la barra pulsanti

La struttura dati ad albero binario

Soluzione dell esercizio del 2 Febbraio 2004

Siamo così arrivati all aritmetica modulare, ma anche a individuare alcuni aspetti di come funziona l aritmetica del calcolatore come vedremo.

Uso dei modelli/template

Gestione Rapporti (Calcolo Aree)

Capitolo 2. Operazione di limite

Programmazione Orientata agli Oggetti in Linguaggio Java

L amministratore di dominio

L interfaccia grafica in Java

APPUNTI DI MATEMATICA LE FRAZIONI ALGEBRICHE ALESSANDRO BOCCONI

Fondamenti di Informatica 1. Prof. B.Buttarazzi A.A. 2010/2011

File, Modifica, Visualizza, Strumenti, Messaggio

PROCEDURA INVENTARIO DI MAGAZZINO di FINE ESERCIZIO (dalla versione 3.2.0)

Corso di Informatica


3 - Variabili. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

Cominciamo dalla barra multifunzione, ossia la struttura a schede che ha sostituito la barra dei menu e la barra delle icone (Figura 1).

4 3 4 = 4 x x x 10 0 aaa

Tipi primitivi. Ad esempio, il codice seguente dichiara una variabile di tipo intero, le assegna il valore 5 e stampa a schermo il suo contenuto:

risulta (x) = 1 se x < 0.

Testo Esercizio. Un modello è ragionevole quando contiene queste tre caratteristiche.

Office 2007 Lezione 07. Gestione delle immagini

f(x) = 1 x. Il dominio di questa funzione è il sottoinsieme proprio di R dato da

Traccia di soluzione dell esercizio del 25/1/2005

Grafico della parabola

RAPPRESENTAZIONE GRAFICA E ANALISI DEI DATI SPERIMENTALI CON EXCEL

Correttezza. Corso di Laurea Ingegneria Informatica Fondamenti di Informatica 1. Dispensa 10. A. Miola Novembre 2007

Amministrazione classi

WORD per WINDOWS95. Un word processor e` come una macchina da scrivere ma. con molte più funzioni. Il testo viene battuto sulla tastiera

Inizializzazione, Assegnamento e Distruzione di Classi

MANUALE UTENTE Fiscali Free

Corso di Informatica

Amministrazione gruppi (all interno della Scuola)

Per effettuare la stampa di una cartella di lavoro si accede al comando. Stampa dal menu File o si utilizza il pulsante omonimo sulla barra

Come costruire una presentazione. PowerPoint 1. ! PowerPoint permette la realizzazione di presentazioni video ipertestuali, animate e multimediali

Funzioni in C. Violetta Lonati

IL MIO PRIMO SITO: NEWS

MANUALE PER L UTILIZZO DELLA FUNZIONE EVENTI Rel.1.2 del 29 gennaio 2004

lo PERSONALIZZARE LA FINESTRA DI WORD 2000

Biblioteca di Cervia NOZIONI BASE DI INFORMATICA

PROCEDURE DI FIRMA PER I PIP PRESENTATI NEI BANDI APPRENDISTATO

5.2.1 RELAZIONI TRA TABELLE Creare una relazione uno-a-uno, uno-a-molti tra tabelle 9

Office 2007 Lezione 08

DESIGN PATTERNS Parte 6. State Proxy

Capitolo II. La forma del valore. 7. La duplice forma in cui si presenta la merce: naturale e di valore.

La grafica con Word. La parte evidenziata è una Riga. La parte evidenziata è una Colonna. La parte evidenziata è una Cella

Visual basic base Lezione 01. L'ambiente di sviluppo

Testo Esercizio. Un modello è ragionevole quando contiene queste tre caratteristiche.

Analisi e diagramma di Pareto

Guida Joomla. di: Alessandro Rossi, Flavio Copes

Main System Monitor Keyboard

Sistema operativo. Sommario. Sistema operativo...1 Browser...1. Convenzioni adottate

Esercitazione di Basi di Dati

INTERFACCE GRAFICHE IN JAVA CON SWING DISPENSE

Corrispondenze e funzioni

PROCESSO DI INDICIZZAZIONE SEMANTICA

L analisi dei dati. Capitolo Il foglio elettronico

Capitolo 4 Pianificazione e Sviluppo di Web Part

Leggere un messaggio. Copyright 2009 Apogeo

Il calendario di Windows Vista

GateManager. 1 Indice. tecnico@gate-manager.it

Fasi di creazione di un programma

LE CARATTERISTICHE DEI PRODOTTI MULTIVARIANTE

NUOVA PROCEDURA COPIA ED INCOLLA PER L INSERIMENTO DELLE CLASSIFICHE NEL SISTEMA INFORMATICO KSPORT.

!"#$%&&'()#*%+%+!"#$"',,'()#*%+ -")%*&'&'+'$.)+-$$%&&) !"#$%&&'(%)'*+%",#-%"#.'%&'#/0)-+#12"+3,)4+56#7+#.')8'9

per immagini guida avanzata Uso delle tabelle e dei grafici Pivot Geometra Luigi Amato Guida Avanzata per immagini excel

Amministrazione gruppi (Comunità)

Esercizi su. Funzioni

Manuale Amministratore Legalmail Enterprise. Manuale ad uso degli Amministratori del Servizio Legalmail Enterprise

Supermarket Progetto di Programmazione Febbraio 2010

Registratori di Cassa

Esercizio 1: trading on-line

IL MIO PRIMO SITO NEWS USANDO GLI SCHEDARI

Soluzione dell esercizio del 12 Febbraio 2004

MOCA. Modulo Candidatura. [Manuale versione 1.0 marzo 2013]

Joomla! 2.5:Utenti e permessi - Il wiki di Joomla.it

Eredità in C++ Corso di Linguaggi di Programmazione ad Oggetti 1. a cura di Giancarlo Cherchi

Object Oriented Programming

- Il sito dedicato alle associazioni no-profit di Milano e provincia

FPf per Windows 3.1. Guida all uso

MANUALE UTENTE Profilo Azienda Partecipata. APPLICATIVO CAFWeb

1. ACCESSO AL DATABASE

UML Diagrammi delle classi. UML Diagramma classi 1

Equilibrio bayesiano perfetto. Giochi di segnalazione

10.1. Un indirizzo IP viene rappresentato in Java come un'istanza della classe InetAddress.

Nell esempio verrà mostrato come creare un semplice documento in Excel per calcolare in modo automatico la rata di un mutuo a tasso fisso conoscendo

PULSANTI E PAGINE Sommario PULSANTI E PAGINE...1

13. Chain of Responsibility

per immagini guida avanzata Organizzazione e controllo dei dati Geometra Luigi Amato Guida Avanzata per immagini excel

Transcript:

Ereditarietà e classi astratte 6 Temi del capitolo 1. Il concetto di ereditarietà 2. Programmazione grafica con ereditarietà 3. Classi astratte 4. Il pattern TEMPLATE METHOD 5. Interfacce protected 6. La gerarchia dei componenti Swing 7. La gerarchia delle forme geometriche standard 8. La gerarchia di classi per le eccezioni 9. Quando non va usata l ereditarietà In questo capitolo presentiamo un importante relazione tra classi: l ereditarietà. Si dice che una classe eredita da un altra classe se ne descrive un sottoinsieme specializzato di oggetti. Ad esempio, una classe Manager può ereditare da una classe Employee: tutti i metodi applicabili ai dipendenti si applicano anche ai dirigenti, ma i dirigenti sono maggiormente specifici, perché hanno metodi che non sono applicabili, in generale, a tutti i dipendenti. Ad esempio, i dirigenti possono beneficiare di premi di cui i normali dipendenti non godono. Partiamo da un esempio molto semplice per mettere in evidenza i meccanismi tipici dell ereditarietà, poi continuiamo con una serie di interessanti programmi grafici che illustrano tecniche più avanzate; infine, analizziamo come viene utilizzata l ereditarietà nella libreria di Java.

242 CAPITOLO 6 Le sottoclassi specializzate ereditano dalle superclassi, che rappresentano concetti più generici. Una sottoclasse può definire ulteriori metodi e campi. 6.1 Il concetto di ereditarietà 6.1.1 Usare l ereditarietà per rendere più specifico un modello Si usa l ereditarietà per costruire un modello di una relazione fra classi in cui una classe rappresenta un concetto più generico, mentre un altra classe rappresenta un concetto più specifico. Ad esempio, considerate una classe Manager che erediti dalla classe Employee: siamo di fronte a un lecito utilizzo dell ereditarietà, perché i dirigenti sono speciali tipi di dipendenti. Ogni dirigente è anche un dipendente, ma non tutti i dipendenti sono anche dirigenti. La classe più generica viene chiamata superclasse, mentre quella più specializzata viene chiamata sottoclasse. In generale, la sottoclasse estende le funzionalità della superclasse e gli oggetti della sottoclasse specializzata possono essere dotati di ulteriori metodi e campi. Considerate questa semplice classe Employee: public class Employee public Employee(String aname) name = aname; public void setsalary(double asalary) salary = asalary; public String getname() return name; public double getsalary() return salary; private String name; private double salary; Le sottoclassi possono sovrascrivere (cioè ridefinire) metodi, fornendo una nuova definizione per un metodo presente anche nella superclasse. Le sottoclassi ereditano i metodi della superclasse. Ecco, invece, una classe Manager che aggiunge un nuovo metodo e un nuovo campo. La sottoclasse sovrascrive (cioè ridefinisce) anche un metodo esistente nella superclasse, fornendone una nuova definizione: la versione del metodo getsalary presente in Manager calcolerà la somma del salario di base e dei premi. public class Manager extends Employee public Manager(String aname) public void setbonus(double abonus) bonus = abonus; // nuovo metodo public double getsalary() // sovrascrive il metodo omonimo presente in Employee private double bonus; // nuovo campo Notate che Java usa la parola chiave extends per indicare l ereditarietà; nel seguito del capitolo troverete il codice per il costruttore di Manager e per il suo metodo getsalary. La Figura 6.1 mostra il diagramma delle classi. Quando si definisce una classe che eredita da una superclasse, bisogna dichiarare soltanto ciò che nella sottoclasse è diverso rispetto alla superclasse. Una sottoclasse eredita automaticamente tutte le caratteristiche della propria superclasse, a meno che la sottoclasse stessa non le sovrascriva.

EREDITARIETÀ E CLASSI ASTRATTE 243 Figura 6.1 La classe Manager eredita dalla classe Employee Nell esempio che abbiamo visto, la sottoclasse Manager ha i metodi e i campi seguenti: I metodi setsalary e getname (ereditati da Employee) Un metodo getsalary (sovrascritto in Manager) Un metodo setbonus (definito in Manager) I campi name e salary (ereditati da Employee) Un campo bonus (definito in Manager) Nota Se un metodo viene contrassegnato dalla parola chiave final non può essere sovrascritto in una sottoclasse: una proprietà utile per metodi di particolare importanza, come, ad esempio, le verifiche relative alla sicurezza. Si può anche definire un intera classe come final: una classe final non può essere estesa. Ad esempio, la classe String è una classe final. 6.1.2 I termini superclasse e sottoclasse Si può restare inizialmente perplessi di fronte ai termini sottoclasse e superclasse usati nell ereditarietà: non è forse vero che un dirigente è, in qualche modo, superiore a un dipendente generico? Dopo tutto, ogni oggetto di tipo Manager contiene tutti i campi di un oggetto di tipo Employee. Perché mai Manager viene definita sottoclasse, mentre Employee ne è la superclasse?

244 CAPITOLO 6 Figura 6.2 L insieme dei dirigenti è un sottoinsieme dell insieme dei dipendenti Gli esemplari delle sottoclassi costituiscono un sottoinsieme all interno dell insieme degli esemplari della superclasse. Questa terminologia dovrebbe risultare assai più chiara pensando a una classe come all insieme dei suoi esemplari. L insieme dei dirigenti è un sottoinsieme dell insieme dei dipendenti, come si vede in Figura 6.2. Insiemi di classi possono costituire complesse gerarchie di ereditarietà. Secondo il principio di sostituzione di Liskov, si può usare un esemplare di una sottoclasse ogni volta che si dovrebbe usare un esemplare di una superclasse. 6.1.3 Gerarchie di ereditarietà Nel mondo reale i concetti vengono spesso catalogati mediante gerarchie, che vengono solitamente rappresentate mediante alberi, con i concetti più generali nella radice della gerarchia e quelli più specifici verso i rami e le foglie. Nella progettazione orientata agli oggetti, analogamente, si è soliti raggruppare le classi in complesse gerarchie di ereditarietà. La Figura 6.3 mostra una parte di una gerarchia di classi che rappresentano diverse tipologie di dipendenti. Alla radice della gerarchia viene posta la classe Object, perché in Java tutte le classi estendono tale classe, come vedrete meglio nel Capitolo 7. Durante la progettazione di una gerarchia di classi, vi chiederete quali siano le caratteristiche comuni a tutte le classi che state progettando: tali proprietà comuni vanno raccolte in superclassi poste alla base della gerarchia. Ad esempio, tutti i dipendenti hanno un nome e un salario. Le proprietà più specifiche, invece, trovano posto solamente nelle sottoclassi: nel nostro modello, ad esempio, soltanto i dirigenti ricevono premi di produzione. Nel seguito del capitolo analizzeremo diverse gerarchie di classi fondamentali che si trovano nella libreria di Java. 6.1.4 Il principio di sostituzione Dal momento che una sottoclasse eredita il comportamento della propria superclasse, potete sempre sostituire un esemplare di una superclasse con un esemplare di una sottoclasse: tale regola viene chiamata principio di sostituzione di Liskov, dal nome di Barbara Liskov, docente al MIT e tra le prime ricercatrici a occuparsi di programmazione orientata agli oggetti.

EREDITARIETÀ E CLASSI ASTRATTE 245 Figura 6.3 Una gerarchia di classi di dipendenti Considerate, ad esempio, le istruzioni che seguono: Employee e; System.out.println( name= + e.getname()); System.out.println( salary= + e.getsalary()); Il principio di sostituzione di Liskov asserisce che le istruzioni funzionanoaltrettanto bene fornendo un esemplare di Manager nel punto in cui è prevista la presenza di un esemplare di Employee. e = new Manager( Bernie Ebbers ); Esaminiamo ora le singole invocazioni. L invocazione e.getname(); non pone problemi, perché la classe Manager eredita il metodo getname dalla classe Employee. Invece, l invocazione e.getsalary();

246 CAPITOLO 6 è più interessante, perché ci sono due versioni del metodo getsalary, una definita nella classe Employee e un altra definita nella classe Manager. Come avete già visto nel Capitolo 4, la macchina virtuale Java esegue automaticamente la corretta versione del metodo, in relazione al tipo di oggetto a cui si riferisce la variabile e: se e contiene un riferimento a un esemplare di Manager, viene invocata la versione del metodo getsalary definita nella classe Manager. Ricordate che questo fenomeno viene chiamato polimorfismo. 6.1.5 Invocazione di metodi della superclasse Realizziamo ora il metodo getsalary della classe Manager, in modo che restituisca la somma tra il salario base e il premio. public class Manager extends Employee public double getsalary() return salary + bonus; // ERRORE campo privato Una sottoclasse non ha accesso alle caratteristiche private della propria superclasse. Abbiamo però un problema: il campo salary è un campo privato della classe Employee e i metodi della classe Manager non hanno diritto di accesso a tale campo. Una sottoclasse deve seguire le stesse regole valide per qualsiasi altra classe: deve quindi usare il metodo pubblico getsalary. public double getsalary() return getsalary() + bonus; // ERRORE invocazione ricorsiva Per invocare un metodo della superclasse si usa la parola chiave super. Ora, sfortunatamente, abbiamo un nuovo e diverso problema: se invocate getsalary all interno di un metodo che si chiama anch esso getsalary, il metodo invoca se stesso. Se, invece, volete invocare il metodo della superclasse per recuperare il valore del salario da dipendente, dovete usare la parola chiave super, in modo da esprimere chiaramente tale vostra intenzione. public double getsalary() return super.getsalary() + bonus; Notate che super non è una variabile e, in particolare, non è simile alla variabile this. Naturalmente, se aveste invocato this.getsalary();

EREDITARIETÀ E CLASSI ASTRATTE 247 allora la versione del metodo getsalary di Manager avrebbe invocato se stessa, dando luogo a una sequenza infinita di invocazioni. Notate anche che non si può banalmente convertire il riferimento this al tipo della superclasse. Considerate questo tentativo: Employee superthis = this; return superthis.getsalary() + bonus; L invocazione superthis.getsalary chiama in causa di nuovo il metodo della classe Manager! Si tratta dell effetto classico del polimorfismo: ciò che determina quale metodo venga chiamato è il tipo effettivo dell oggetto a cui si riferisce la variabile e non il tipo dichiarato per la variabile stessa. Dato che l oggetto a cui si riferisce superthis è di tipo Manager, viene invocato il metodo getsalary della classe Manager. La parola chiave super surclassa il meccanismo di invocazione polimorfica e provoca forzatamente l invocazione del metodo della superclasse. Per invocare un costruttore della superclasse si usa la parola chiave super come prima istruzione all interno del costruttore della sottoclasse. Suggerimento Come avete già visto, le sottoclassi non hanno diritto di accesso alle caratteristiche private della propria superclasse. A volte i principianti tentano di risolvere questo problema ridefinendo i campi all interno della sottoclasse, in questo modo: public class Manager extends Employee private double salary; // ERRORE campo duplicato A questo punto un oggetto di tipo Manager ha due campi di nome salary! Uno di essi viene gestito dai metodi di Employee e l altro dai metodi di Manager. Evitate con grande attenzione questo errore abbastanza frequente. 6.1.6 Invocazione di costruttori della superclasse Per portare a termine la realizzazione della classe Manager, dobbiamo fornire il codice del suo costruttore. Tale costruttore riceve una stringa che contiene il nome del dirigente: come abbiamo già detto, non si può semplicemente impostare il campo name al valore di tale parametro, perché tale campo è un campo privato della classe Employee. Bisogna, invece, invocare il costruttore di Employee e, anche in questo caso, si usa la parola chiave super: public Manager(String aname) super(aname); // invoca il costruttore della superclasse bonus = 0;

248 CAPITOLO 6 Se un costruttore di una sottoclasse non invoca un costruttore della superclasse, viene automaticamente invocato il costruttore della superclasse privo di parametri. L invocazione del costruttore della superclasse deve essere il primo enunciato del costruttore della sottoclasse. Se la superclasse ha un costruttore senza parametri, allora il costruttore di una sottoclasse non deve obbligatoriamente invocare un costruttore della superclasse: in mancanza di tale invocazione, viene automaticamente invocato il costruttore della superclasse privo di parametri. Ad esempio, nel seguito di questo capitolo definiremo sottoclassi di JPanel e di JFrame: dato che tali classi hanno costruttori privi di parametri, i costruttori delle sottoclassi non hanno bisogno di invocare super. Se, invece, tutti i costruttori della superclasse richiedono parametri, allora la sottoclasse deve invocare super, altrimenti il compilatore segnalerà un errore. Un metodo sovrascritto in una sottoclasse può soltanto imporre una pre-condizione che sia al massimo restrittiva quanto quella imposta dal metodo che sovrascrive. 6.1.7 Pre-condizioni e post-condizioni dei metodi ereditati Ricordate, dal Capitolo 3, che una pre-condizione di un metodo è una condizione che deve essere vera prima che venga invocato il metodo e chi invoca il metodo ha la responsabilità di effettuare l invocazione soltanto quando la pre-condizione sia verificata. Quando una sottoclasse sovrascrive un metodo, la relativa pre-condizione non può essere più restrittiva della pre-condizione del metodo della superclasse che viene sovrascritto. Ad esempio, imponiamo una pre-condizione ragionevole per il metodo setsalary della classe Employee: il salario deve essere un valore positivo. public class Employee /** Imposta il salario del dipendente al valore assegnato. @param asalary il nuovo salario @precondition asalary > 0 */ public void setsalary(double asalary) Prendiamo ora in considerazione la sottoclasse Manager: è possibile che nella classe Manager venga imposta al metodo setsalary una pre-condizione che imponga al salario di essere sempre non inferiore a $100.000? No, perché la pre-condizione di un metodo di sottoclasse non può essere più restrittiva della pre-condizione presente nel metodo della superclasse che viene sovrascritto. Per capire il motivo di questa regola, considerate questi enunciati. Employee e = ; e.setsalary(50000); Questo codice potrebbe sembrare corretto, dal momento che il parametro del metodo è maggiore di zero, così soddisfacendo la pre-condizione del metodo della classe Employee. Tuttavia, se e facesse riferimento a un oggetto di tipo Manager, verrebbe vio-

EREDITARIETÀ E CLASSI ASTRATTE 249 Un metodo sovrascritto in una sottoclasse deve garantire una post-condizione che sia almeno ampia quanto quella imposta dal metodo che sovrascrive. lata la pre-condizione che impone al salario di non essere inferiore a $100000: ciò è in aperta contraddizione con il concetto, che abbiamo precedentemente esposto, secondo il quale le pre-condizioni devono sempre essere verificabili dal programmatore che si accinge a effettuare l invocazione di un metodo. Per riassumere, quindi: quando una sottoclasse sovrascrive un metodo, la sua precondizione può essere al massimo tanto restrittiva quanto la pre-condizione del metodo della superclasse. In particolare, se un metodo di una superclasse non ha pre-condizioni, nemmeno il metodo della sottoclasse può avere pre-condizioni. Di converso, quando una sottoclasse sovrascrive un metodo, la sua post-condizione deve essere almeno tanto ampia quanto la post-condizione del metodo della superclasse. Ad esempio, supponiamo che Employee.setSalary garantisca di non diminuire il salario di un dipendente: di conseguenza, tutti i metodi che sovrascrivono setsalary devono fornire la medesima garanzia, oppure una più ampia. Nota Avete appena visto che le pre-condizioni di metodi di una sottoclasse non possono essere più restrittive delle pre-condizioni dei metodi della superclasse che vengono sovrascritti: per un certo numero di altre condizioni si possono fare i medesimi ragionamenti. In particolare: Quando sovrascrivete un metodo, non potete renderlo meno accessibile. Quando sovrascrivete un metodo, non potete lanciare eccezioni a gestione obbligatoria che non siano già dichiarate nel metodo della superclasse che viene sovrascritto. 6.2 Programmazione grafica con ereditarietà 6.2.1 Progettare sottoclassi della classe JPanel In questa sezione vedrete l ereditarietà applicata a situazioni pratiche di programmazione. Nel Capitolo 4 avete visto come disegnare forme grafiche usando classi che realizzano l interfaccia Icon. public class MyIcon implements Icon public void painticon(component c, Graphics g, int x, int y) qui si inseriscono le istruzioni che disegnano

250 CAPITOLO 6 Per disegnare forme grafiche si possono progettare sottoclassi di JPanel, sovrascrivendone il metodo paintcomponent. Una diversa tecnica, molto usata, consiste nella definizione di una sottoclasse di un componente quale JPanel, ridefinendone il metodo paintcomponent in questo modo: public class MyPanel extends JPanel public void paintcomponent(graphics g) qui si inseriscono le istruzioni che disegnano Questo approccio presenta un vantaggio: la classe JPanel ha un vasto insieme di caratteristiche e comportamenti che vengono così ereditati. Ad esempio, potete associare al pannello un ascoltatore di eventi del mouse e ricevere le corrispondenti notifiche quando l utente preme il pulsante del mouse trovandosi sul pannello stesso. Quella appena evidenziata è una differenza importante che si ha quando si estende una classe, piuttosto che realizzare un interfaccia. Quando realizzate un interfaccia, partite dal nulla: l interfaccia vi fornisce solamente i nomi e le firme dei metodi che dovete realizzare; quando, invece, estendete una classe, ereditate tutte le caratteristiche offerte dalla superclasse. Svilupperemo ora un programma che consenta a un utente di spostare la sagoma di un automobile trascinandola col mouse e, nelle sezioni successive, tale programma verrà migliorato visualizzando un ambientazione contenente forme geometriche arbitrarie. La classe CarPanel memorizza un riferimento a una sagoma di automobile e il suo metodo paintcomponent disegna tale sagoma: public class CarPanel extends JPanel public void paintcomponent(graphics g) // metodo incompleto Graphics2D g2 = (Graphics2D) g; car.draw(g2); private CarShape car; Il metodo paintcomponent di una sottoclasse di JPanel deve invocare il metodo della propria superclasse perché quest ultimo cancelli lo sfondo del pannello. Questo metodo paintcomponent ha, però, un problema: se un esemplare di CarPanel viene aggiunto a un frame e tale frame viene visualizzato, allora tutto sembra a posto e l automobile viene disegnata, ma se a questo punto si sposta un altra finestra al di sopra della finestra del nostro programma, ciò che viene visualizzato appare confuso, come si vede in Figura 6.4. Il problema del nostro codice grafico consiste nella mancata cancellazione di quanto contenuto in precedenza nel pannello: ciò è compito del metodo paintcomponent della superclasse JPanel, che dobbiamo invocare prima di compiere nuove operazioni grafiche:

EREDITARIETÀ E CLASSI ASTRATTE 251 Figura 6.4 Un pannello con visualizzazione confusa public void paintcomponent(graphics g) super.paintcomponent(g); Dopo aver aggiunto l invocazione di super.paintcomponent, il pannello viene ridisegnato correttamente. Per avere notizia degli eventi relativi al mouse, si associano a un componente un ascoltatore di eventi del mouse e un ascoltatore di eventi di movimento del mouse. 6.2.2 Interfaccia per ascoltatori di eventi e classi adattatrici Per completare il programma che disegna automobili dobbiamo aggiungere gestori di eventi del mouse. Quando l utente preme il pulsante del mouse, vogliamo verificare se il puntatore del mouse si trova all interno della sagoma dell automobile, perché soltanto in tal caso inizieremo il processo di trascinamento della sagoma, inseguendo la posizione del mouse finché l utente tiene premuto il pulsante. Per abilitare il tracciamento degli eventi del mouse, si associa un opportuno ascoltatore al pannello; in realtà esistono due tipi di tali ascoltatori, uno per le pressioni dei pulsanti del mouse e uno per i movimenti del mouse stesso. Tali due tipi di eventi vengono distinti perché porsi in ascolto degli eventi di movimento del mouse è molto dispendioso: gli utenti dei programmi spostano il mouse molto frequentemente, provocando moltissime invocazioni degli ascoltatori dei relativi eventi: se una procedura è interessata ai soli eventi di pressione di un pulsante del mouse, è preferibile che non usi anche un ascoltatore per gli eventi di movimento del mouse stesso.

252 CAPITOLO 6 Ecco le interfacce di cui stiamo parlando: public interface MouseListener void mouseclicked(mouseevent event); void mousepressed(mouseevent event); void mousereleased(mouseevent event); void mouseentered(mouseevent event); void mouseexited(mouseevent event); public interface MouseMotionListener void mousemoved(mouseevent event); void mousedragged(mouseevent event); Le interfacce per ascoltatori di eventi aventi molti metodi hanno corrispondenti classi adattatrici dotate di metodi che non fanno nulla: invece di realizzare direttamente le interfacce, estendete gli adattatori. Le interfacce MouseListener e MouseMotionListener hanno, entrambe, parecchi metodi, anche se di solito un ascoltatore di eventi vuole compiere un azione significativa soltanto in corrispondenza di uno o due di tali eventi. Per semplificare la realizzazione degli ascoltatori, un anima generosa ha progettato due classi, MouseAdapter e Mouse- MotionAdapter, che realizzano tutti i metodi dei relativi ascoltatori in modo che non compiano alcuna azione. Ecco, ad esempio, il codice della classe MouseAdapter: public class MouseAdapter implements MouseListener public void mouseclicked(mouseevent event) public void mousepressed(mouseevent event) public void mousereleased(mouseevent event) public void mouseentered(mouseevent event) public void mouseexited(mouseevent event) Per definire un vostro ascoltatore di eventi, potete semplicemente estendere queste classi adattatrici e sovrascrivere solamente i metodi che vi interessano. Ad esempio, l ascoltatore di eventi del mouse per la classe CarPanel è interessato alle sole invocazioni di mousepressed e non agli altri quattro metodi dell interfaccia MouseListener. Suggerimento Se la maggior parte delle sottoclassi può usare una versione comune di un metodo, mentre soltanto alcune necessitano di una versione diversa, spostate nella superclasse il metodo più diffuso. Le poche sottoclassi che hanno bisogno di una diversa versione del metodo lo possono sovrascrivere, mentre tutte le altre sottoclassi non dovranno far nulla.