Oggetti e Classi in Java - Dipartimento di Ingegneria dell`Informazione
Transcript
Oggetti e Classi in Java - Dipartimento di Ingegneria dell`Informazione
M.P. PARTE I Corso Metodologie di Programmazione ( Ling.di Prog.:Metod.di Progr.) Oggetti e Classi in Java a.a.2002-2003 Docente : prof.B: Venneri Lucidi delle lezioni relative al Modulo II : Programmazione orient ata agli oggetti: Java 1 2 Oggetti Un oggetto rappresenta un dato, ed è costituito da stato: collezione di variabili (attributi) comportamento: collezione di operazioni (metodi) Ogni oggetto ha un’interfaccia e ogni oggetto ha un tipo Esempio - contatore STATO int c; METODI void iniz(int i) void incr() void decr() int val() 3 inizializza il contatore a i incrementa il contatore di 1 decrementa il contatore di 1 restituisce il valore del contatore 4 1 NOTA Contatore Invio di Messaggi Variabile locale Passaggio by value void iniz( int i) void incr( ) void decr( ) int val( ) int c; v oid iniz( int i) { c= 1; } v oid incr( ) { + + c; } v oid decr( ) { - - c; } int v al( ) { ret urn c; } invio di messaggio = invocazione di un metodo I n Java, come in altri linguaggi, si usa la notazione con il punto: oggetto.metodo(…)(anche per selezionare il campo attributo) cont1 3 I NTERFACCI A I MPLEMENTAZI ONE cont2 10 CONTATORE int x,y; cont1.incr(); cont2.decr(); x = cont1.val(); y = cont2.val(); x ha valore 4 e y ha valore 9 5 THIS Forma generale di una Classe class nome-classe { *tipo v1; *tipo v2; ……… *tipo vn; In un linguaggio tradizionale, per eseguire una operazione su un dato, dovremmo passare il dato come parametro: incr(cont1) o iniz(cont1, 3) Nella programmazione ad oggetti il dato è l’oggetto implicito destinatario del messaggio ( this = parametro implicito) 6 cont1.incr() o cont1.iniz(3) *tipo nomemetodo1(elenco-parametri) {corpo del metodo} ……… *tipo nomemetodoN(elenco-parametri) {corpo del metodo} } dove * è lo specificatore di accesso del membro seguente 7 8 2 Esempio: contatore Esempio :contatore La classe definisce un tipo Istanza di una classe = oggetto In Java: class Contatore { int c; void iniz(int i) {c = i;} void incr() {++c;} void decr() {--c;} Nota: this implicito int val() {return c;} } In ++c, c sta per this.c I st anze cont 1 cont 2 3 10 9 Oggetti come parametri 10 Information Hiding Per realizzare information hiding si usano le parole riservate public - interfaccia private - implementazione di fronte ad ogni membro della classe I l parametro formale di un metodo può essere un oggetto L’oggetto, parametro attuale, può essere modificato. class Contatore { int c; void swap(Contatore altro) {int temp=c; c=altro.c; altro.c=temp;} …………………………… class Contatore { private int c; public void iniz(int i) {c = i;} public void incr() {++c;} public void decr() {--c;} public int val() {return c;} } Siano c1 e c2 due contatori: c1.swap(c2) L’effetto ottenuto è che c1 e c2 hanno relamente scambiato il loro valore 11 12 3 Controllo dell’accesso Accesso alle classi Specificatori per i membri delle classi: Una classe ha solo due livelli di accesso possibili ( lo specificatore è premesso alla parola class ) public è accessibile da qualunque parte del programma (anche da codice di altri pacchetti) private è visibile solo all’interno della stessa classe protected è visibile a tutte le classi dello stesso pacchetto e solo alle sottoclassi di pacchetti diversi default (nessuna specifica esplicita di accesso) è visibile a tutte le classi dello stesso pacchetto ma non alle classi di altri pacchetti public accessibile da qualunque codice default accessibile solo da codice all’interno dello stesso pacchetto 13 Creare un’istanza 14 Recupero della memoria new Contatore() Garbage collection (Scheme, Prolog, Java) Il programmatore puo’ solo allocare dati dinamici Crea un nuovo oggetto di tipo Contatore e ne restituisce la referenza ( handle) Tutti gli oggetti sono allocati dinamicamente (quando si esegue la new) e sono manipolati attraverso una handle (puntatore/ referenza) 15 Una procedura di sistema, il garbage collector, si preoccupa di recuperare tutte le aree di memoria nello heap non più raggiungibili in modo da poterle riutilizzare 16 4 Come si inizializza un oggetto Si può chiamare esplicitamente un metodo di inizializzazione: Contatore cont1; cont1 = new Contatore(); cont1.iniz(3); Java (come C+ + ) fornisce la nozione di costruttore, che consente di inizializzare automaticamente un oggetto al momento della creazione. Costruttori class Contatore { int c; Contatore(int i) {c = i;} …… } Cost rut t ore Ha lo stesso nome della classe! Non ha tipo di ritorno ( è implicito il tipo della classe stessa)! Contatore cont1 = new Contatore(3) crea un contatore - lo inizializza a 3. 17 18 Overloading di metodi Overloading di costruttori Overloading: metodi diversi possono avere lo stesso nome (nello stesso campo di visibilità) I metodi "overloaded" si distinguono uno dall’altro in base alla lista e ai tipi dei parametri ( non in base al tipo di ritorno) Il compilatore, quando incontra una chiamata al metodo, distingue di quale versione si tratta in base al matching dei parametri attuali-formali 19 E’ opportuno avere diversi costruttori “overloaded” in ogni classe Esempio: class Contatore { int c; Contatore() {c = 0;} Contatore(int i) {c = i;} …… } 20 5 Chiamata del costruttore La parola chiave static Contatore cont1 = new Contatore(3) Contatore cont1 = new Contatore() La parola static può essere usata per identificare un membro di una classe che verrà utilizzato senza riferimento a un’istanza della classe: svolge il ruolo di una variabile globale, tutte le istanze di quella classe condividono la stessa variabile di classe Esempio: I n una classe BankAccount, vogliamo fare in modo che il costruttore assegni il numero di conto in modo progressivo (1,2,…):tenere l’ultimo numero di conto assegnato. Nel primo caso viene chiamato il costruttore con parametro intero, nel secondo il costruttore senza parametri Java crea un costruttore di default senza parametri per le classi per le quali non è disponibile alcun costruttore quello di default non è più disponibile se è definito un costruttore! 21 22 Esempio di variabile di classe Metodi static public class BankAccount { …….. private int accountNumber; private static int last=0 BankAccount () {// genera il numero di conto successivo last++; //aggiorna l’oggetto accountNumber=last ;} …… } Per chiamare un metodo (così come una variabile) static dall’esterno della sua classe, si usa la solita notazione con punto preceduto dal nome della classe nomeclasse.metodo() Questi metodi possono chiamare solo altri metodi static, possono accedere solo a variabili static,etc…, ossia non hanno nessun riferimento a un this implicito. Attenzione: le variabili statiche non sono, in genere, desiderate. 23 24 6 Variabili final Classi interne Una variabile può essere dichiarata “final” per impedire la modifica del so contenuto (convenzione: lettere maiuscole per i loro nomi, come per le costanti). Quando è necessaria una classe per uno scopo molto tattico e limitato, si può dichiarare questa inner-classe all’interno del metodo che ne ha bisogno. Spesso, in questi casi, si vuole solo usare un esemplare di questa classe per gli scopi del metodo: è possibile definire una classe anonima. il significato di final è del tutto diverso per i metodi! 25 26 CLASSI ASTRATTE La parola chiave abstract Definizione Una classe astratta è una classe che contiene almeno un metodo non implementato I n Java ogni metodo astratto può essere evidenziato con il prefisso abstract la classe che ha (eredita) almeno un metodo astratto (senza body) deve essere etichettata con abstract (ma non viceversa) Es: abstract class Figura { ……. public abstract void disegna(); …… } Ruolo : serve come base di una derivazione, fattorizza comportamenti comuni a più classi, etc.., non può essere istanziata 27 28 7 INTERFACCE Interfaccia vs.Classe Definizione Una INTERFACE è una classe totalmente astratta : non contiene attributi e tutti i metodi sono astratti Ruolo : dichiara un insieme di metodi con le loro signature, quindi definisce l’interfaccia comune di diverse classi derivate (enfatizza separazione fra interfaccia e implementazione) In Java: una classe concreta che deriva da un’interfaccia implementa l’interfaccia (derivazione particolare) Tutti i metodi sono automaticamente abstract Tutti i metodi sono automaticamente public Non contiene attributi Sintassi Java public interface NomeInterfaccia { Elenco metodi con signature } Esercizio: interfaccia per Pile e Code PARTE II Ereditarietà 29 Derivazione, Ereditarietà e Polimorfismo 30 class Finestra { Rettangolo r; .... void disegnaCornice() {...} void disegnaContenuto() {...} } class FinestraConTitolo extends Finestra { String titolo; void disegnaTitolo() {...} } 31 32 8 Sottoclassi Extends (Java) Classe = Modulo FinestraConTitolo estende Finestra E‘ la parola chiave per definire una gerarchia di classi class ClassePadre { […] } -eredita attributi e metodi class ClasseFigliaUno extends ClassePadre { […] } class ClasseFigliaDue extends ClassePadre { […] } -aggiunge attributi e metodi -riscrive metodi 33 Sottotipo 34 Controllo statico dei tipi Java, come molti altri linguaggi, effettua un controllo dei tipi (type checking) statico. Derivazione di classe= Specializzazione del tipo Statico: fatto dal compilatore prima di iniziare l’esecuzione del programma. Dinamico: fatto dall’interprete durante l’esecuzione ( a runtime) cla ss B e x t e n ds A A B x B x A 35 Il type checking statico garantisce che non ci saranno errori durante l’esecuzione. 36 9 Controllo statico dei tipi Finestra f; FinestraConTitolo ft; ... ft.disegnaCornice(); f.disegnaCornice(); ft.disegnaTitolo(); f.disegnaTitolo(); Ereditarietà singola Ogni sottoclasse ha una sola sopraclasse. Struttura ad albero. corretto I n Java la classe Object è la radice della gerarchia. errore di compilazione f è una finestra e non ha il metodo disegnaTitolo Qualunque oggetto è un Object. Type checking statico: il compilatore controlla che per una variabile si chiami un metodo definito (ereditato) per la classe di quella variabile 37 Ereditarietà multipla 38 Polimorfismo Articolo codice prezzo ApparecchioElettrico tensione potenza I tipi primitivi non sono Object. POLI MORFISMO: proprietà di un oggetto di avere più di un tipo, in accordo alla relazione di ereditarietà. Esempio: D sottoclasse di B e di A un oggetto D è un B ed è un A un oggetto di tipo (classe) D è anche un oggetto di tipo (classe) B e anche un oggetto di tipo (classe) A Televisore pollici In Java non è permessa. Deve essere trasformata con relazione d’uso 39 40 10 Polimorfismo A d b a a a; B b; D d; = new D(); = new D(); = new D(); = b; Type Matching L’assegnamento x = espr è corretta per il compilatore se: quest i assegnam ent i sono t ut t i legali perché un ogget t o di t ipo D ha anche t ipo B e t ipo A Analogamente se x è un parametro formale di un metodo e espr è il parametro attuale (della chiamata) 41 Upcasting Controllo statico. 42 Upcasting Upcasting: ci si muove da un tipo specifico ad uno più generico (da un tipo ad un suo “sopratipo”). Tipo di espr è uguale a tipo di x Tipo di espr è sottotipo del tipo di x L’upcasting è sicuro per il type checking: dato che una sottoclasse eredita tutti i metodi delle sue sopraclassi, ogni messaggio che può essere inviato ad una sopraclasse può anche essere inviato alla sottoclasse senza il rischio di errori durante l' esecuzione. 43 Poligono p; Rettangolo r; ... p.disegna(); r.disegna(); p.perimetro(); r.perimetro(); corretto per il compilatore 44 11 Upcasting Overriding Poligono p; Rettangolo r; p = r; r.diagonale(); corretto errore di compilazione p è di tipo Poligono e non ha il metodo diagonale 45 Riscrivere un metodo Spesso un metodo di una sottoclasse definito per overriding non ridefinisce completamente il metodo della sua sopraclasse, ma lo estende. Ad esempio, il metodo disegnaCornice della FinestraConTitolo estende il metodo disegnaCornice della Finestra, con del codice specifico per disegnare il titolo. Si deve MANTENERE LA STESSA SIGNATURA class Finestra { Rettangolo r; .... void disegnaCornice() {...} void disegnaContenuto() {...} } p.diagonale(); se si facesse il controllo a runtime, sarebbe corretto perché p è legata ad un rettangolo (che possiede il metodo diagonale). Una sottoclasse può anche riscrivere (OVERRI DI NG) un metodo della sopraclasse. Per evitare di duplicare il codice, si può far riferimento ad un metodo della sopraclasse con lo stesso nome mediante la notazione 47 super. class FinestraConTitolo extends Finestra { String titolo; void disegnaCornice() { … disegna la cornice con il titolo …} } 46 super class FinestraConTitolo extends Finestra { String titolo; void disegnaCornice() { super.disegnaCornice(); ... nuovo codice ... } } super.disegnaCornice() chiam a il m et odo disegnaCornice della sopraclasse ( che alt rim ent i non sarebbe visibile, causa lookup dei m et odi dal basso) Not a Senza super si avrebbe un loop infinit o! 48 12 Cella Costruttori in classi derivate class Cella { int contenuto=0; int get() {return contenuto;} void set(int n) {contenuto=n;} } class CellaConMemoria extends Cella { int backup=0; void set(int n) {backup=contenuto; super.set(n);} void restore() {contenuto=backup;} } Il costruttore della classe derivata deve richiamare il costruttore della classe base per inizializzare i campi ereditati Java inserisce automaticamente una chiamata al costruttore della classe base nel costruttore della classe derivata (prima azione) 49 Polimorfismo: perdita d’ informazione Super come costruttore Super è utilizzata per chiamare un costruttore della sopraclasse La chiamata di this(… ) o super(…) deve essere la prima istruzione del costruttore! class ClasseSopra { public ClasseSopra(int x) { System.out.println("Costruttore ClasseSopra: " + x); } public ClasseSopra() { this(-1); System.out.println("Costruttore ClasseSopra"); } } class ClasseSotto extends ClasseSopra { public ClasseSotto (int x) { super(x); System.out.println("Costruttore ClasseSotto"); } public ClasseSotto() { super(); super(); System.out.println("ostruttore ClasseSotto"); } } 50 51 Poligono p; Rettangolo r; ... p = r; p.disegna(); p.perimetro(); corretto per il compilatore, ma quale metodo si esegue? Quello di Poligono o quello di Rettangolo? 52 13 Binding dinamico Binding dinamico Un oggetto decide quale metodo applicare a se stesso in base al proprio tipo BI NDI NG DI NAMICO: la forma di un oggetto determina dinamicamente quale versione di un metodo applicare Polimorfismo + Binding dinamico Oggetti diversi, ma con una interfaccia comune, sono trattati in modo omogeneo, ma ciascuno può rispondere a una chiamata di metodo in modo specifico p.perimetro(); Si esegue il metodo perimetro dell’oggetto a cui p fa riferimento in quel momento. Poligono p = new Poligono(); si esegue il metodo Rettangolo r = new Rettangolo(); perimetro di Poligono p.perimetro(); p = r; si esegue il metodo perimetro di Rettangolo p.perimetro(); 53 54 Uso del binding dinamico Lookup dei metodi class Finestra { Rettangolo r; .... void disegnaCornice() {...} void disegnaContenuto() {...} void rinfresca() { disegnaCornice(); disegnaContenuto(); } } Ricerca di un metodo per un oggetto: la sottoclasse verifica se possiede o meno un metodo con tale nome e con gli stessi parametri (stessa signatura), se si‘ , lo usa se no, si cerca nella classe padre… (e cosi` via nella gerarchia) Un metodo definito in una sottoclasse e avente la stessa signatura di un metodo di una delle classi antenate nasconde il metodo di quest’ultima alla sottoclasse class FinestraConTitolo extends Finestra { String titolo; void disegnaCornice() { … disegna la cornice con il titolo …} } 55 56 14 Overloading vs. overriding class ClasseA { public void metodo (int x) { System.out.println("Metodo di Classe A: " + x); } OVERLOADING public void metodo() { System.out.println("Metodo di Classe A"); this.metodo(-1); } } OVERRIDING OVERLOADING class ClasseB extends ClasseA { public void metodo (int x) { System.out.println("Metodo di Classe B esteso"); super.metodo(x); } OVERLOADING public void metodo() { System.out.println("Metodo di Classe B esteso"); super.metodo(); } } Overriding di metodi privati Overloading: stesso nome ma diversa lista dei tipi di parametri Overriding: stessa firma ma classi diverse (purché nella stessa gerarchia) class ClasseUno { public void metodo1 () { System.out.println("Eseguito metodo 1 nella classe Uno"); this.metodo2(); } private void metodo2() { System.out.println("Eseguito metodo 2 nella classe Uno"); } class ClasseDue extends ClasseUno { public void metodo2 () { System.out.println("Eseguito metodo 2 nella classe Due"); } } La parola chiave final Se invio il messaggio metodo1 ad un oggetto di tipo ClasseDue quale metodo metodo2 viene eseguito? Binding dinamico? Dichiarare che un metodo è privato equivale a “cambiargli nome” A parte ClasseUno nessuno sa o deve sapere dell’esistenza di metodo2 di ClasseUno 58 Ereditarietà e Accesso } 57 Anche se la sottoclasse comprende tutti i membri della sopraclasse, non può accedere ai membri dichiarati private nella sopraclasse Un membro di una classe dichiarato private rimane tale anche per le sottoclassi Java non consente di riscrivere un metodo per restringerne l’accesso (es. errore se public nella superclasse e privat e nella sottoclasse) 59 Come modificatore all’inizio della dichiarazione di un metodo: il metodo non può essere riscritto nelle sottoclassi (risoluzione con binding statico) Come modificatore davanti alla dichiarazione di una classe : la classe non può essere ereditata (implicitamente tutti i suoi metodi sono final) Una classe non può essere contemporaneamente abstract e final ! 60 15 Derivazione e classi astratte Derivazione e Interfacce Da una classe astratta possiamo derivare altre classi astratte e/ o classi concrete (valgono tutte le regole della normale derivazione di classi) Un’ interfaccia può ereditare da altre interfacce utilizzando la parola chiave extends Interface Prima { void m1(); void m2(); } Interface Seconda extends Prima{ void m3(); } Uso: delle classi astratte posso dichiarare referenze a oggetti, anche se non posso creare oggetti uso del polimorfismo L’interfaccia Seconda include la Prima 61 Implementazione di Interfacce 62 Una classe può implementare un’I nterfaccia con la parola chiave implements Interfacce vs. classi astratte Le classi astratte possono essere miste, ossia possono contenere anche metodi non astratti. class MyClass implements Seconda {deve implementare m1, m2 e m3 } Ereditarietà singola per le classi, anche astratte, ma una classe può implementare più interfacce 1. MyClass deve implementare anche i metodi ereditati! 2. Polimorfismo: Seconda obj = new MyClass(); obj.m2(); 3. I metodi che implementano i metodi dell’interfaccia • devono essere dichiarati “public” • devono avere la stessa signatura dell’interfaccia 63 Una classe può implementare più di una interfaccia e contemporaneamente estendere una classe ( parziale ereditarietà multipla, matrimonio di convenienza) 64 16 Interfacce multiple interface A { void metodoA(); } Verso l’ereditarietà multipla interface B { void metodoB(); } Una classe può implementare più interfacce senza i problemi standard della multipla ereditarietà: non ci sono attributi (quindi non c’è il problema della loro duplicazione). non ci possono essere problemi di name clash di metodi, perché i metodi ereditati sono astratti. class C implements A,B { public void metodoA() {System.out.println("sono A");} public void metodoB() {System.out.println("sono B");} public static void main(String[] args) { A a = new C();//l’oggetto è visto come un A B b = new C();//l’oggetto è visto come un B a.metodoA(); b.metodoB(); a.metodoB(); //errore: A non ha il metodoB } } 65 66 Horror Show (un esempio) Interface Mostro { void minaccia(); } Interface MostroPericoloso extends Mostro { void distrugge(); } Interface Letale { void uccide(); } Interface Vampiro extends MostroPericoloso,Letale { void bevesangue(Mostro m); } 67 Horror Show (continua) Class Dragone implements MostroPericoloso { public void minaccia() {…}; public void distrugge() {…}; } Class Dracula implements Vampiro { public void minaccia() {…}; public void distrugge() {…}; public void uccide() {…}; public void bevesangue(Mostro m) {…}; } 68 17 Horror Show (continua) Interfacce per viste diverse Class HorrorShow { static void azione1(Mostro m) {m.minaccia()}; static void azione2(MostroPericoloso mp) {mp.minaccia(); mp.distrugge}; public static void main (…) { Dragone animale = new Dragone(); Vampiro vp = new Dracula(); azione1(animale); azione2(animale); vp.bevesangue(animale) } Interface CanSwim {void nuota();} Interface CanFly {void vola();} Class Azione {public void combatte();} Class Eroe extends Azione implements CanSwim, CanFly …………………… Un Eroe è visto come CanSwim o come CanFly, a seconda che sia riferito da una variabile di tipo CanSwim o di tipo CanFly (“è visto” = insieme dei servizi offerti) 69 70 Betti BettiVenneri: Venneri: 13-11-02 13-11-02 Un esempio Un esempio (continua) Un oggetto DataSet gestisce dei valori in ingresso, aggiungendo, tenendo il massimo, calcolando la media etc… public class DataSet {// Costruisce un insieme di dati vuoto. // Aggiunge un valore all’insieme dei dati. public void add(double x) { sum = sum + x; if (count == 0 || maximum < x) maximum = x; count++; } …… //restituisce il massimo public double getMaximum() { return maximum; } public DataSet() { sum = 0; count = 0; maximum = 0; private double sum; private double maximum; private int count; } } 71 72 18 Sviluppare codice riutilizzabile -La classe DataSet potrebbe essere utilizzata anche per ottenere il minimo , il massimo, etc.. su conti bancari . Supponiamo di avere una classe BankAccount con un campo “Saldo” e un metodo “getSaldo” che restituisce questo valore Usare Interfacce per migliorare il riuso E per analizzare le “monete”? Soluzione: invece di avere tante classi DataSet, utilizziamo un’interfaccia comune “Misurabile” tale che monete, BankAccount, etc…siano tutti “Misurabili” public class DataSet Bisogna modificare la classa DataSet solo per cambiare il tipo dei dati: {// Aggiunge un valore all’insieme dei dati. public void add(Misurabile x) { sum = sum + x.getMisura; if (count == 0 || maximum.getMisura < x.getMisura) maximum = x; count++; } public void add(BankAccount x) public BankAccount getMaximum() private BankAccount maximum; Ma il resto del codice resta uguale! 73 Usare Interfacce…(continua) 74 Usare Interfacce…(continua) ….. public interface Misurabile {double getMisura() } Public class BankAccount implements Misurabile {…… public double getMisura { return saldo; } …} //Restituisce il massimo public Misurabile getMaximum() { return maximum; } private double sum; private Misurabile maximum; private int count; } } 75 Così anche le classi “moneta” etc devono implementare Misurabile, ossia fornire la propria implementazione di getMisura 76 19 Aggiungere Interfacce strategiche Usare Interfacce…(continua) Il disaccoppiamento fra BankAccount,Moneta,etc. e DataSet rende riutilizzabile DataSet DataSet BankAccount Problema : Posso modificare le classi BankAccount,… solo perché sono definite da me “interface” Misurabile Posso “misurare” un oggetto in un unico modo Soluzione: la responsabilità della misurazione non deve ricadere sugli oggetti, è meglio che un altro oggetto effettui le misurazioni. Ci sarà un oggetto-misuratore per ogni tipo di dati, questi misuratori implementano un’interfaccia comune Measurer. Moneta 77 78 Aggiungere Interfacce…(continua) Aggiungere Interfacce…(continua) File Measurer.java /** Descrive una qualsiasi classe i cui oggetti possano misurare altri oggetti. */ public interface Measurer { /** Calcola la misura di un oggetto. anObject è l’oggetto da misurare return la misura */ double measure(Object anObject); } La classe DataSet è ora costruita con un oggetto m di tipo Measurer, a cui sono demandate le operazioni di misurazione Per ottenere un misuratore dei BankAccount ci sarà la classe opportuna Class BankMeasurer implements Measurer che implementa il suo metodo measure (attenzione: il parametro è object,quindi sarà necessario un downcast esplicito!) 79 80 20 File DataSet.java public class DataSet { /*Costruisce un insieme vuoto di dati con un misuratore assegnato. aMeasurer è il misuratore che viene usato per misurare i valori dei dati */ public DataSet(Measurer aMeasurer) { sum = 0; count = 0; maximum = null; measurer = aMeasurer; } /** Aggiorna il massimo e aggiunge il dato */ public void add(Object x) { sum = sum + measurer.measure(x); if (count == 0 || measurer.measure(maximum) < measurer.measure(x)) maximum = x; count++; } ……………. 81 82 Polimorfismo Parametrico Tipi polimorfi = Tipi con un parametro di tipo Esempio: Pila [ t] Il comportamento della Pila può essere definito per un generico tipo t degli elementi /** Restituisce il dato maggiore tra i dati inseriti. */ public Object getMaximum() { return maximum; } private private private private (ortogonale al polimorfismo per sottotipo) double sum; Object maximum; int count; Measurer measurer; } 83 84 21 Classi generiche Classi Parametriche in C+ + La proprietà che tutte le classi sono discendenti di Object consente di definire strutture dati che possono contenere class Pila { oggetti di qualunque ... tipo. void push(Object x) Template <T> Class Pila …… Void push (T elemento)…. Per utilizzare una pila di BankAccount: Pila <BankAccount> piladiconti; Piladiconti.push(contoRossi); {...}; Object pop() {...}; Non si può specificare il } tipo degli elementi della pila: possono essere oggetti qualunque. PROBLEMA Gli oggetti della Pila sono solo Object! 85 86 Downcasting in Java class Pila { ... void push(Object x) {...}; Object pop() {...}; } Downcasting Downcasting: ci si muove da un tipo più generale ad uno più specifico (da un tipo ad un sottotipo) Supponiamo di sapere che sulla pila vengono messi solo rettangoli. Come possiamo utilizzare gli oggetti estratti dalla pila? Se A è un sottotipo di B e se espr ha tipo B, ( A) espr ha tipo A Pila s; Rettangolo r; ... s.push(new Rettangolo()); r = s.pop(); Java non supporta la programmazione Generica (si usa Object ma è richiesto un cast) C+ + mette a disposizione i Template: L’assegnamento Errore di compilazione. pop restituisce un Object, che è più generale di Rettangolo 87 A a = (A) espr è corretto per il compilatore (A) espr può dare errore a run time, se l’oggetto ottenuto valutando espr non ha tipo A. 88 22 Downcasting PARTE III class Pila { ... void push(Object x) {...}; Object pop() {...}; } ... Pila s; Rettangolo r; ... s.push(new Rettangolo()); r = (Rettangolo) s.pop(); Argomenti di approfondimento Accettato dal compilatore. Può dare errore a run time. Controllo a run-time. Quando si esegue questa istruzione si controlla che l' oggetto restituito da pop sia veramente un rettangolo. 89 La classe di tutte le classi 90 La classe Object In Java ciascuna classe che non estende altre classi estende automaticamente la classe Object : Object è la radice di ogni gerarchia di derivazione. La classe Obj ect si trova nel pacchetto java.lang, che viene automaticamente imporatato in ogni programma Dunque, ogni oggetto è anche un Object 91 L’utilità della classe Object consiste non solo nella possibilità di usare referenze Object per oggetti di qualunque classe ma anche nella possibilità di usare/riscrivere alcuni metodi, che in Object sono presenti ma molto generici. Alcuni metodi utili: toString, equals, clone 1) toString : restituisce una stringa che descrive l’oggetto su cui è invocato ( di fatto è invocato ogni volta che concatenate “+ ” una stringa con un oggetto) Es: BankAccount marioRossi = new BankAccount(); String s= marioRossi.toString(); 92 23 getClass ( + getName ) toString s non è molto utile , si riferisce a qualcosa come BankAccountd1229bf ossia nome della classe e un indirizzo di memoria Esempio di Overriding del metodo: Public class BankAccount implements Misurabile {…… public String toString() {return “BankAccount[saldo=“+ saldo +”]” ; } …} Adesso con String s = marioRossi.toString(); s si riferisce alla stringa “BankAccount[saldo=3000]” UTILE quando volete controllare l’esecuzione del programma final Class getClass: ottiene un oggetto di tipo Class usiamo anche il metodo getName che, invocato su un oggetto di tipo Class, restituisce il nome della classe Esempio: Public class BankAccount {…… public String toString() {return getClass().getName()+ “[saldo=“+ saldo +”]” ; …} } 93 Per Esercitazione Il metodo equals 2) Equals: Boolean equals (Object obj) Esercizio per prossima esercitazione: sia AccountUno una sottoclasse di BankAccount AccountUno mio=…..; System.out.println (mio); 94 Restituisce true se l’oggetto su cui si invoca è uguale a obj, nel senso che contiene la stessa informazione. Per controllare lo stato corrente dell’oggetto, facciamo la stampa…. stampiamo il nome della sottoclasse o quello della sopraclasse? Se vogliamo stampare oltre al nome della sottoclasse anche un nuovo attributo introdotto in AccountUno? 95 Si può riscrivere, ma dobbiamo di nuovo usare un cast! 96 24 Il metodo clone Esempio di clonazione Clonare = fare una copia di un oggetto 3) metodo Clone ( ) della classe Object Protected Quindi deve essere forzato a public nella classe che si vuole rendere clonabile (es. public Object clone() = return super.clone() ) Restituisce un tipo Object Dunque può richiedere downcast sul risultato La classe clonabile deve implementare l’interfaccia “Cloneable” : se chiamo obj.clone() e la classe di obj non implementa Cloneable , è sollevata un’eccezione NOTA : Bisogna gestire l’eccezione associata Esempio class Nuova implements Cloneable { public Object Clone() { Object ob = null; try { ob = super.clone;} catch (CloneNotSupportedException e) { return null oppure e.stampa qualcosa;} return ob; } } Clone esegue ( definisce lo spazio necessario per eseguire) una copia bit a bit dell’oggetto 97 Clonazione e Derivazione 98 Copia Profonda Ora tutte le classi derivate da Nuova sono automaticamente clonabili (a meno che non sia disattivata la clonazione) Attenzione: devo ridefinire clone se non voglio che venga usato quello della superclasse! 99 La Clone esegue una copia superficiale: ad es., se alcuni campi contengono referenze ad altri oggetti, questi risultano condivisi dall’oggetto clonato e dal suo clone Programmare l’operazione di copia profonda: ma non posso sempre sapere se le classi degli oggetti interni sono Clonabili (in caso negativo, è sollevata l’eccezione) (Alternativa: ottenere la copia profonda usando la serializzazione dgli oggetti) 100 25 SERIALIZZAZIONE Esempio di serializzazione Problema: persistenza degli oggetti fra un’esecuzione e l’altra Java offre una semplice soluzione fornendo il meccanismo Serializzazione: un qualunque oggetto di una classe, che implementi l’interfaccia Serializable, può essere convertito in una sequenza di byte e ripristinato (su / da file: si usano flussi di oggetti invece che flussi binari “import java.io” metodo w riteObject (della classe ObjectOutputStream) metodo readObject (della classe ObjectI nputStream) che, però, restituisce tipo Object class BankAccount implements Serializable {………….} …….. ObjectOutputStream out = new ObjectOutputStream ( new FileOutputStream (“dato”) ); out.writeObject (mioconto); out.close; ObjectInputStream in = new ObjectInputStream ( new FileInputStream (“dato”) ); BankAccount mioconto = (BankAccount) in.readObject() in.close; Se si esegue la serializzazione in un unico flusso, la rete degli oggetti è salvata senza nessuna modifica: mettete i vostri dati in un contenitore e, alla fine, salvate questo contenitore ! 101 102 RTTI La Riflessione I n fase di esecuzione posso avere informazioni sul tipo dell’oggetto Possibilità di ottenere informazioni sulle classi in fase di esecuzione Ad es: Class c = x.getClass( ) c è un oggetto Class (la classe di x) c. getName restituisce il nome della classe oggettoclasse isI stance ( nomeoggetto) restituisce true se l’oggetto è istanza della classe (il file .class viene aperto ed esaminato in esecuzione) La classe Class supporta la Riflessione Java.lang.reflect mette a disposizione alcuni metodi come getFields( ) , getMethods( ) ,…. per ottenere array degli oggetti che rappresentano i campi, i metodi, …..di una classe (esempi di uso della Riflessione nel cap.12 del testo) 103 104 26 Documentazione del codice APPENDICE I l programma java.doc genera automaticamente un insieme di pagine HTML che descrivono (documentano) le classi. Per ogni metodo: commento di documentazione: incluso fra / * * e * / @param nome del parametro e spiegazione @return descrizione del valore restituito Per ogni classe: Commento di documentazione USO: il comando javadoc nomeclasse.java (anche prima della compilazione) produce il file “ nomeclasse.html “ che descrive l’interfaccia della classe (e che si può esaminare con un browser) O.O.P. Programmazione/ Progettazione per Contratto Una classe descrive un’astrazione uniforme sulle proprietà di un insieme di oggetti; queste proprietà sono l’insieme degli stati in cui gli oggetti possono trovarsi e il loro comportamento I nvariante di classe: asserzione che gli oggetti della classe soddisfano in ogni momento (dopo la loro costruzione, dopo la chiamata di un modificatore): VI NCOLI DI CONSI STENZA 105 106 Invarianti di Classi e… Asserzioni dei metodi Precondizioni e Postcondizioni per ogni metodo: Precondizione: responsabilità per chi chiama il metodo Postcondizione: responsabilità per chi ha progettato il metodo Derivazione di B da A: per ogni metodo m di A riscritto in B: i) la sua precondizione in B non è più restrittiva di quella in A (può essere più “larga”) ii) viceversa per la postcondizione covarianza- controvarianza Es: invariante di stack numElementi> = 0 & numElementi< = dimensionestack Derivazionedi B da A: B eredita l’invariante e aggiunge specifiche I nv.B I nv.A SpaziostatiB è incluso in SpaziostatiA ComportamentoB estende ComportamentoA Correttezza di una Classe Una classe è corretta rispetto alle sue asserzioni sse: 1. Per ogni metodo m { I NV & prec-di-m} bodym { I NV & postc-di-m} 2. Dato il costruttore C { prec-di-C} bodyC { I NV} 107 108 27 Covarianza-Controvarianza Principio della sostitutività: Ogni oggetto/ codice di tipo più piccolo può essere usato dove ne è richiesto uno di (ha anche) tipo più grande Regola di sottotipo per le funzioni: Sia f: A →B: A’→B’ ⊆ A →B se e solo se A ⊆A’ e B’ ⊆B. Dunque la regola corretta per permettere di cambiare la signatura di f nella sua riscrittura nella sottoclasse è quella di riscriverla con un tipo più piccolo A’→B’ , ossia Controvarianza dell’argomento : il tipo dei parametri cresce Covarianza del risultato : il tipo del risultato decresce 109 28