Post on 16-Nov-2014
description
Effective Java - Item 10
By
Ferdous Mahmud Shaon and
Hasan Shihab Uddin
Software Engineer,
Escenic Bangladesh.
Item 10: Override clone judiciously
Cloneable interface clone() method shallow vs. deep cloning
// consider the following class:
public class Swimmer {
private String name;
private float swimTime;
private Date eventDate;
public Swimmer() …
public String getName() { return name; }
public float getSwimTime() { return swimTime; }
public Date getEventDate() { return eventDate; }
}
assume:assume:default default constructor reads constructor reads data from filedata from file
Problem: breaks Problem: breaks encapsulation!encapsulation!
// application programmer writes:
Swimmer cu1 = new Swimmer();
Date d = cu1.getEventDate();
d.setYear(1982);
// problem!! Date is mutable
cu1 Swimmer
String
d
name
swimTime
eventDate
26.31
Kristen
Dateyear 2005
etc.1982
Immutable
mutable
Aliasing problem both d and cu1.eventDate refer to the
same exact object so, if you change one, you change the
other solution: accessor should return a
“clone” (copy) of the event date is this a problem for the name field? clone() makes a copy of an object …
the question is: what kind of copy?
protected Object clone() throws CloneNotSupportedException
Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object. The general intent is that, for any object x:
x.clone() != xwill be true, and that the expression:
x.clone().getClass() == x.getClass()will be true, but these are not absolute requirements. While it is typically the case that:
x.clone().equals(x)will be true, this is not an absolute requirement.
Object.clone()
makes a field-by-field, bit-by-bit copy of a cloneable object
throws CloneNotSupportedException for any class which doesn’t implement cloneable
Cloneable interface the Cloneable interface is a “marker”
interface – it has no methods!
the purpose of a marker interface is to allow you to use instanceof in a type inquiry:
if (x instanceof Cloneable) // clone allowed
repeat: clone() is not in the Cloneable interface; the clone() method is in class Object
Example: Class Die (not implementing Cloneable)
Die d1 = new Die();
Die d2 = (Die) d1.clone(); // ERROR
need to do the following:
1. implement Cloneable interface
2. override Object.clone() method
note the cast (since clone returns note the cast (since clone returns Object)Object)
public class Die implements Cloneable{ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(e.toString()); } }// etc.
calls calls Object.clone()Object.clone()
can’t happen, since Die is can’t happen, since Die is CloneableCloneable
Cloning swimmer:
It’s not enough to use the Object cloning method, since it only makes a “shallow” copy
public class Swimmer implements Cloneable{ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(e.toString()); } }// etc.
Cloning swimmers:Swimmer s1 = new Swimmer();Swimmer s2 = (Swimmer) s1.clone();
What’s the problem?
- Have used the shallow copy. So the references still point to the same exact objects. Not a problem for immutable objects, but is problematic for mutable objects
public class Swimmer implements Cloneable {
public Object clone() { try {
Swimmer s = (Swimmer) super.clone(); s.eventDate = (Date) eventDate.clone(); return s;
} catch (CloneNotSupportedException e) {
throw new InternalError(e.toString()); } }
makes a makes a bit-by-bit-by-bit copybit copy
copies the copies the DateDate
It is up to the class designer to determine whether:
1. the default clone() is good enough:
“Shallow cloning”
2. the default clone() can be overriden by calling clone on each non-primitive, mutable instance variable:
“Deep cloning”
3. give up – don’t use cloning
Case 1: shallow cloningDefault clone is good enough:
All instance variables are either primitive values or, and every superclass up to immutable objects Object is well-behaved
implements Cloneable interface in the class, define a method:
public Object clone() that calls
super.clone() and returns that copy
public class X implements Cloneable{ public Object clone() { try { return super.clone(); // shallow clone } catch (CloneNotSupportedException e) { throw new InternalError(e.toString()); } }
Case 2: deep cloning
The default clone can be patched up: implement Cloneable interface in your class, define a method:
public Object clone()
that calls super.clone() to make a shallow copy, plus makes deep copies of fields that refer to mutable objects by calling clone() individually on those fields
public class X implements Cloneable{ private int a; // primitive private String b; // reference to immutable object private Y c; // where class Y has mutator methods private Z d; // where class Z has mutator methods
public Object clone() { // deep clone try { X other = (X) super.clone(); // fields a & b OK other.c = (Y) c.clone(); // fix c by making a copy other.d = (Z) d.clone(); // fix d by making a copy return other; // return the deep clone } catch (CloneNotSupportedException e) { throw new InternalError(e.toString()); }
public class Swimmer implements Cloneable{ private String name; private float swimTime; private Date eventDate; public Swimmer() … public String getName() { return name; } public float getSwimTime() { return swimTime; } public Object clone() { /* case 2: deep clone */ }
public Date getEventDate() {
return (Date) eventDate.clone(); // note: Date is cloneable }
}
fix the fix the accessor by accessor by returning a returning a copycopy
public class A implements Cloneable { public HashMap map;
public A() { map = new HashMap(); map.put("key1", "value1"); map.put("key2", "value2"); }
public Object clone() { try { A other = (A)super.clone(); other.map = (HashMap)map.clone(); return other;
} catch (CloneNotSupportedException e) { throw new InternalError(e.toString()); } }
}
Case 3: don’t use cloning
Consider providing a copy constructor or a static factory method … copy constructor – is simply a constructor that takes a single argument whose type is the class containing the constructor, for example,
public Yum(Yum yum);
A minor variant is to provide a static factory in place of a constructor:
public static Yum newInstance(Yum yum);
Advantages over cloning…
Cloneable/clone: They do not rely on a risk-prone extralinguistic object creation
they do not demand unenforceable adherence to ill-documented conventions;
they do not conflict with the proper use of final fields; they do not require the client to catch
an unnecessary checked exception; and they provide a statically typed object to the
client. So type-casting
public StudioGroup(final Group pGroup) {
mId = pGroup.getId();
setName(pGroup.getName());
setDescription(pGroup.getName());
Status status = pGroup.getStatus();
setStatus(status == null ? null : new StudioStatus(status));
List<Category> categories = pGroup.getCategories();
setCategories(categories == null ? null :
new ArrayList<Category>(categories));
setHotTopicsAccessible(pGroup.isHotTopicsAccessible());
}
public Object clone() throws CloneNotSupportedException {
Group group = (Group) super.clone();
Status status = group.getStatus();
group.setStatus(status == null ? null : (Status) status.clone());
List<Category> categories = group.getCategories();
group.setCategories(categories == null ? null : (List<Category>) categories.clone());
return group;
}
Given all of the problems associated with Cloneable, it is safe to say that other interfaces should not extend it and that classes designed for inheritance should not implement it.
Be aware that if you do not at least provide a well-behaved protected clone method on a class designed for inheritance, it will be impossible for subclasses to implement Cloneable.
Thank You