Java: equals e clone

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