09/04/2014 Java: tipi (im)mutaibili. Metodi equals e clone Mutabilità Un tipo è mutabile (mutable) se ha dei metodi che possono modificare ilrep, altrimenti è detto immutabile (immutable). della classe! Il metodo è mutator già nella specifica Come decidere fra mutable e immutable? Perché IntSet mutable e Poly immutable? 2 1 09/04/2014 Mutable meglio per oggetti che modellano entità del mondo reale: automobili, persone, ecc. che cambiano dinamicamente poco sicuro se oggetti condividono parti, ma efficiente quindi lo usiamo per IntSet perché pensiamo che dovremo spesso mettere o togliere elementi: molto più efficiente se non dobbiamo ad ogni inserzione copiare il contenuto di un IntSet in un nuovo oggetto e Richiede syncronized, lock ecc. per essere thread-safe 3 Immutable Usato sempre se oggetti possono comparire in espressioni, come ad es. oggetti espressioni non hanno side-effect spiacevoli... Es. supponiamo che Poly sia mutabile e add sia così definita: //@ ensures q!=null && (* this == \old(this) + q *); //@ signals (NullPointerException e) q == null; public Poly add(Poly q) throws NullPointerException se p = 3x, che cosa vale p dopo p.add(p).add(p)? è come p+p+p? e p.add(p.add(p))? è come p+(p+p)? Da usare se oggetti devono potere condividere parti, o per non usare sync. In programmazione multithread poco efficiente se occorre spesso modificare oggetti: per cambiare un oggetto occorre farne una copia con produttori, con perdita di tempo e appesantimento del garbage collector. Tipicamente, i tipi immutabili sono dichiarati /*@ pure @*/ in JML (ma ciò non basta a garantire immutabilità...) 4 2 09/04/2014 Come definire oggetti immutabili Definire tutti gli attributi di istanza final e private però se servono effetti coll. benevoli allora possono non essere final Se gli attributi di istanza hanno riferimenti a oggetti mutabili non esporre le parti mutabili del rep Se si vuole che anche gli oggetti delle sottoclassi restino immutabili, non consentire alle sottoclassi di fare override dei metodi basta dichiarare la classe final 5 Il metodo public Object clone() Il metodo clone() restituisce una copia del suo oggetto, nello stesso stato. clone bitwise copy) ma normalmente clone non è accessibile (protected): se si vuole che classe fornisca clone, occorre scrivere: implements Cloneable e definire un metodo clone (che ad esempio richiama il clone definito in Object), altrimenti chiamata a clone() provoca eccezione CloneNotSupportedException: public class IntSet implements Cloneable{ public Object clone() {return super.clone();} IntSet s = new IntSet(); s.insert(2); IntSet t = (IntSet) s.clone(); //clone restituisce un Object: serve cast clone() può esser definito ad hoc quando la definizione di Object non va 6 3 09/04/2014 Quando ridefinire clone()? clone() (se serve usarlo) va ridefinito se la classe è mutabile. Es. IntSet s = new IntSet(); IntSet t = (IntSet) s.clone();s.insert(2); System.out.println(t.toString()); la clone() di default (in Object) copia campo per campo, cioè: t= (IntSet) s.clone(); è come t = new IntSet(); t.els =s.els; Quindi s e t hanno stesso valore per campo els di tipo ArrayList: sharing! Oggetti mutabili non possono condividere parti, pena grossi guai! Implementazione corretta per IntSet: private IntSet(ArrayList v) {els = v;} public Object clone() { return new IntSet((ArrayList) els.clone()); } si basa sul fatto che clone() di ArrayList fa copia elemento per elemento 7 Problemi con clone() di Object e tipi mutabili (shallow copy) paolo p Persona padre = setEta setPadre Persona padre = eta = 020 setEta setPadre Persona padre = 30 eta = 040 setEta eta = 020 setPadre Persona p = (Persona) paolo.clone(); //usa bitwise clopy //modifiche a p.padre sono modifiche a paolo.padre, anche se paolo e p sono oggetti distinti! 8 4 09/04/2014 Differenza con deep copy paolo p Persona padre = setPadre setEta setPadre Persona padre = padre = eta = 020 setEta Persona Persona 30 eta = 040 setEta padre = eta = 020 setPadre 40 eta = 030 setEta setPadre Persona p = (Persona) paolo.clone(); //ora usa deep clopy 9 clone() e tipi immutabili Non è invece necessario ridefinire clone se la classe è immutabile! Se classe è immutabile, basta una sola copia dei suoi oggetti Poly p = new Poly(3,2); Poly q = p; q è copia di p, anche se sono condivisi: oggetto riferito da p (e quindi p non può essere cambiato! quindi se clone() serve per tipi immutabili (ad es. perché si richiamano metodi che si aspettano degli oggetti clonabili), basta implementare un metodo che richiama quello predefinito in Object o addirittura che fa copia del reference 10 5 09/04/2014 Metodo equals della classe Object -tipi della classe Object, per cui è predefinite le operazioni public boolean equals (Object z) implementazione di default: confronto fra reference: due oggetti sono equals solo se sono lo stesso oggetto dei metodi della classe Object o se è meglio ri-definirli!!! Nel caso di equals occorre una certa Lo standard Java richiede che: public boolean equals(Object obj) indicates whether some other object is "equal to" this one. The equals method implements an equivalence relation on non-null object references: It is reflexive: for any non-null reference value x, x.equals(x) should return true. It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true. It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true. It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified. For any non-null reference value x, x.equals(null) should return false. 11 Quale eguaglianza rappresentare in equals? equals dovrebbe implementare uguaglianza di comportamento (behavioral equivalence): due oggetti sono equals se non è possibile distinguerli usando una sequenza di invocazioni di metodi della classe su uno dei due oggetti Motivo della scelta: altrimenti sorgono problemi con collezioni di oggetti mutabili che usano ad esempio equals per cercare un oggetto (si vedrà meglio con astrazioni polimorfe) Scelta poco seguita: in SDK ad esempio il metodo per la classe List è definito così: public boolean equals(Object o) compares the specified object with this list... Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of iterator in the two lists are equal. ... Questa definizione non è equivalente alla precedente!! Due liste sono equals a un certo punto, ma possono diventare !equals perche un elemento! 12 6 09/04/2014 equals con oggetti immutabili Due oggetti immutabili sono equals sse hanno lo stesso stato astratto Motivo: due oggetti immutabili non possono essere distinti tramite mutazioni; inoltre gli observer della classe permettono solo di distinguere gli oggetti se hanno un diverso stato astratto (altrimenti le osservazioni non danno differenza) p e q non possono essere distinti tramite mutazioni: equals deve dare true quindi equals va ridefinito se la classe è immutabile (i reference sono diversi ma oggetti sono equals). Es. con String, equals controlla uguaglianza carattere per carattere a.equals(b) dà true NB: due oggetti (immutabili) concreti diversi con lo stesso stato astratto sono equals!! 13 Es. Implementazione equals Il tipo immutabile Point2D rappresenta punti in spazio 2D class Point2D { private int x; private int y; public boolean equals (Point2D p) { return (x==p.x && y==p.y) } public boolean equals (Object z) { if (!(z instanceof Point2D)) return false; return equals((Point2D ) z); } } definisce un equals che restituisce true sse due punti hanno le stesse coordinate xey overloading) utile per maggiore efficienza: se compilatore equals su Point2D, non si deve fare instanceof + casting. 14 7 09/04/2014 il metodo equals e gli oggetti mutabili Due oggetti mutabili sono equals se e solo se sono lo stesso oggetto: List<Integer> s = new ArrayList<integer>(); List<Integer> t = new ArrayList<integer>(); if (s.equals s e t non sono equals anche se hanno lo stesso stato, perché sono distinguibili attraverso mutazioni: dopo s.insert(2), s e t hanno stati diversi! quindi equals non andrebbe ridefinito se la nuova classe è mutabile (es ArrayList) perché questa è proprio la definizione data in Object. Se serve eguaglianza più debole, definire un metodo ad hoc (come similar: oggetti indistinguibili usando solo observers, cioè stesso stato astratto) ad esempio, s.similar(t) deve dare true se s e t rappresentano liste uguali Questo principio è spesso ignorato in pratica (p.es. in tutte le librerie di Java), e può causare errori con i contenitori. Ad es., ArrayList ridefinisce equals anche se ArrayList è tipo mutabile. 17 Riassunto equals e clone Metodo Tipo Mutabile Tipo Immutabile equals Accettare definizione di Object (uguaglianza dei reference) (poco usato in pratica) Ridefinirlo come uguaglianza di stato astratto clone Ridefinirlo come copia dello stato astratto, se serve che sia disponibile Richiamare definizione di Object (copia del reference), se serve che sia disponibile 18 8
© Copyright 2025 ExpyDoc