effective java - override clone() method judiciously

27
Effective Java - Item 10 By Ferdous Mahmud Shaon and Hasan Shihab Uddin Software Engineer, Escenic Bangladesh.

Upload: ferdous-mahmud

Post on 16-Nov-2014

7.364 views

Category:

Education


3 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Effective Java - Override clone() method judiciously

Effective Java - Item 10

By

Ferdous Mahmud Shaon and

Hasan Shihab Uddin

Software Engineer,

Escenic Bangladesh.

Page 2: Effective Java - Override clone() method judiciously

Item 10: Override clone judiciously

Cloneable interface clone() method shallow vs. deep cloning

Page 3: Effective Java - Override clone() method judiciously

// 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!

Page 4: Effective Java - Override clone() method judiciously

// application programmer writes:

Swimmer cu1 = new Swimmer();

Date d = cu1.getEventDate();

d.setYear(1982);

// problem!! Date is mutable

Page 5: Effective Java - Override clone() method judiciously

cu1 Swimmer

String

d

name

swimTime

eventDate

26.31

Kristen

Dateyear 2005

etc.1982

Immutable

mutable

Page 6: Effective Java - Override clone() method judiciously

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?

Page 7: Effective Java - Override clone() method judiciously

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.

Page 8: Effective Java - Override clone() method judiciously

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

Page 9: Effective Java - Override clone() method judiciously

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

Page 10: Effective Java - Override clone() method judiciously

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)

Page 11: Effective Java - Override clone() method judiciously

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

Page 12: Effective Java - Override clone() method judiciously

Cloning swimmer:

It’s not enough to use the Object cloning method, since it only makes a “shallow” copy

Page 13: Effective Java - Override clone() method judiciously

public class Swimmer implements Cloneable{ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(e.toString()); } }// etc.

Page 14: Effective Java - Override clone() method judiciously

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

Page 15: Effective Java - Override clone() method judiciously

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

Page 16: Effective Java - Override clone() method judiciously

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

Page 17: Effective Java - Override clone() method judiciously

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

Page 18: Effective Java - Override clone() method judiciously

public class X implements Cloneable{ public Object clone() { try { return super.clone(); // shallow clone } catch (CloneNotSupportedException e) { throw new InternalError(e.toString()); } }

Page 19: Effective Java - Override clone() method judiciously

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

Page 20: Effective Java - Override clone() method judiciously

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()); }

Page 21: Effective Java - Override clone() method judiciously

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

Page 22: Effective Java - Override clone() method judiciously

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()); } }

}

Page 23: Effective Java - Override clone() method judiciously

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);

Page 24: Effective Java - Override clone() method judiciously

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

Page 25: Effective Java - Override clone() method judiciously

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;

}

Page 26: Effective Java - Override clone() method judiciously

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.

Page 27: Effective Java - Override clone() method judiciously

Thank You