Effective Java - Override clone() method judiciously

8,945
-1

Published on

Published in: Education, Technology, Spiritual
1 Comment
4 Likes
Statistics
Notes
  • Thanks for the great and well-explained content!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
8,945
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
61
Comments
1
Likes
4
Embeds 0
No embeds

No notes for slide

Effective Java - Override clone() method judiciously

  1. 1. Effective Java - Item 10 <ul><ul><li>By </li></ul></ul><ul><ul><li>Ferdous Mahmud Shaon and </li></ul></ul><ul><ul><li>Hasan Shihab Uddin </li></ul></ul><ul><ul><li>Software Engineer, </li></ul></ul><ul><ul><li>Escenic Bangladesh. </li></ul></ul>
  2. 2. Item 10: Override clone judiciously <ul><li>Cloneable interface </li></ul><ul><li>clone() method </li></ul><ul><li>shallow vs. deep cloning </li></ul>
  3. 3. <ul><li>// consider the following class: </li></ul><ul><li>public class Swimmer { </li></ul><ul><li>private String name; </li></ul><ul><li>private float swimTime; </li></ul><ul><li>private Date eventDate; </li></ul><ul><li>public Swimmer() … </li></ul><ul><li>public String getName() { return name; } </li></ul><ul><li>public float getSwimTime() { return swimTime; } </li></ul><ul><li>public Date getEventDate() { return eventDate; } </li></ul><ul><li>} </li></ul>assume: default constructor reads data from file Problem: breaks encapsulation!
  4. 4. <ul><li>// application programmer writes: </li></ul><ul><li>Swimmer cu1 = new Swimmer(); </li></ul><ul><li>Date d = cu1.getEventDate(); </li></ul><ul><li>d.setYear(1982); </li></ul><ul><li>// problem!! Date is mutable </li></ul>
  5. 5. Swimmer String name swimTime eventDate 26.31 Kristen Date year 2005 etc. 1982 Immutable mutable cu1 d
  6. 6. Aliasing problem <ul><li>both d and cu1.eventDate refer to the same exact object </li></ul><ul><li>so, if you change one, you change the other </li></ul><ul><li>solution: accessor should return a “clone” (copy) of the event date </li></ul><ul><li>is this a problem for the name field? </li></ul><ul><li>clone() makes a copy of an object … the question is: what kind of copy? </li></ul>
  7. 7. protected Object clone () throws CloneNotSupportedException <ul><li>Creates and returns a copy of this object. The precise meaning of &quot;copy&quot; may depend on the class of the object. The general intent is that, for any object x: </li></ul><ul><li>x.clone() != x </li></ul><ul><li>will be true, and that the expression: </li></ul><ul><li>x.clone().getClass() == x.getClass() </li></ul><ul><li>will be true, but these are not absolute requirements. While it is typically the case that: </li></ul><ul><li>x.clone().equals(x) </li></ul><ul><li>will be true, this is not an absolute requirement. </li></ul>
  8. 8. Object.clone() <ul><li>makes a field-by-field, bit-by-bit copy of a cloneable object </li></ul><ul><li>throws CloneNotSupportedException for any class which doesn’t implement cloneable </li></ul>
  9. 9. Cloneable interface <ul><li>the Cloneable interface is a “ marker ” interface – it has no methods ! </li></ul><ul><li>the purpose of a marker interface is to allow you to use instanceof in a type inquiry: </li></ul><ul><li>if (x instanceof Cloneable) // clone allowed </li></ul><ul><li>repeat: clone() is not in the Cloneable interface; the clone() method is in class Object </li></ul>
  10. 10. Example: Class Die ( not implementing Cloneable ) <ul><li>Die d1 = new Die(); </li></ul><ul><li>Die d2 = (Die) d1.clone(); // ERROR </li></ul><ul><li>need to do the following: </li></ul><ul><li>implement Cloneable interface </li></ul><ul><li>override Object.clone() method </li></ul>note the cast (since clone returns Object)
  11. 11. <ul><li>public class Die implements Cloneable </li></ul><ul><li>{ </li></ul><ul><li>public Object clone() { </li></ul><ul><li>try </li></ul><ul><li>{ </li></ul><ul><li>return super.clone(); </li></ul><ul><li>} </li></ul><ul><li>catch (CloneNotSupportedException e) </li></ul><ul><li>{ </li></ul><ul><li>throw new InternalError(e.toString()); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>// etc. </li></ul>calls Object.clone() can’t happen, since Die is Cloneable
  12. 12. Cloning swimmer: <ul><li>It’s not enough to use the Object cloning method, since it only makes a “ shallow ” copy </li></ul>
  13. 13. <ul><li>public class Swimmer implements Cloneable </li></ul><ul><li>{ </li></ul><ul><li>public Object clone() { </li></ul><ul><li>try </li></ul><ul><li>{ </li></ul><ul><li>return super.clone(); </li></ul><ul><li>} </li></ul><ul><li>catch (CloneNotSupportedException e) </li></ul><ul><li>{ </li></ul><ul><li>throw new InternalError(e.toString()); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>// etc. </li></ul>
  14. 14. Cloning swimmers: <ul><li>Swimmer s1 = new Swimmer(); </li></ul><ul><li>Swimmer s2 = (Swimmer) s1.clone(); </li></ul><ul><li>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 </li></ul>
  15. 15. <ul><li>public class Swimmer implements Cloneable { </li></ul><ul><li>public Object clone() { </li></ul><ul><li>try { </li></ul><ul><li>Swimmer s = (Swimmer) super.clone(); </li></ul><ul><li>s.eventDate = (Date) eventDate.clone(); </li></ul><ul><li>return s; </li></ul><ul><li>} catch (CloneNotSupportedException e) { </li></ul><ul><li>throw new InternalError(e.toString()); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>makes a bit-by-bit copy copies the Date
  16. 16. It is up to the class designer to determine whether: <ul><li>the default clone() is good enough: </li></ul><ul><li>“ Shallow cloning ” </li></ul><ul><li>the default clone() can be overriden by calling clone on each non-primitive, mutable instance variable: </li></ul><ul><li>“ Deep cloning ” </li></ul><ul><li>give up – don’t use cloning </li></ul>
  17. 17. Case 1: shallow cloning <ul><li>Default clone is good enough: </li></ul><ul><li>All instance variables are either primitive values or, and every superclass up to immutable objects Object is well-behaved </li></ul><ul><li>implements Cloneable interface </li></ul><ul><li>in the class, define a method: </li></ul><ul><li> public Object clone() that calls </li></ul><ul><li>super.clone() and returns that copy </li></ul>
  18. 18. <ul><li>public class X implements Cloneable </li></ul><ul><li>{ </li></ul><ul><li>public Object clone() { </li></ul><ul><li>try </li></ul><ul><li>{ </li></ul><ul><li>return super.clone(); // shallow clone </li></ul><ul><li>} </li></ul><ul><li>catch (CloneNotSupportedException e) </li></ul><ul><li>{ </li></ul><ul><li>throw new InternalError(e.toString()); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  19. 19. Case 2: deep cloning <ul><li>The default clone can be patched up: </li></ul><ul><li>implement Cloneable interface </li></ul><ul><li>in your class, define a method: </li></ul><ul><li>public Object clone() </li></ul><ul><li>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 </li></ul>
  20. 20. <ul><li>public class X implements Cloneable </li></ul><ul><li>{ private int a; // primitive </li></ul><ul><li>private String b; // reference to immutable object </li></ul><ul><li>private Y c; // where class Y has mutator methods </li></ul><ul><li>private Z d; // where class Z has mutator methods </li></ul><ul><li>public Object clone() { // deep clone </li></ul><ul><li>try { </li></ul><ul><li>X other = ( X ) super.clone(); // fields a & b OK </li></ul><ul><li>other.c = (Y) c.clone(); // fix c by making a copy </li></ul><ul><li>other.d = (Z) d.clone(); // fix d by making a copy </li></ul><ul><li>return other; // return the deep clone </li></ul><ul><li>} catch (CloneNotSupportedException e) { </li></ul><ul><li>throw new InternalError(e.toString()); </li></ul><ul><li>} </li></ul>
  21. 21. <ul><li>public class Swimmer implements Cloneable { </li></ul><ul><li>private String name; </li></ul><ul><li>private float swimTime; </li></ul><ul><li>private Date eventDate; </li></ul><ul><li>public Swimmer() … </li></ul><ul><li>public String getName() { return name; } </li></ul><ul><li>public float getSwimTime() { return swimTime; } </li></ul><ul><li>public Object clone() { /* case 2: deep clone */ } </li></ul><ul><li>public Date getEventDate() { </li></ul><ul><li>return (Date) eventDate.clone(); </li></ul><ul><li>// note: Date is cloneable </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>fix the accessor by returning a copy
  22. 22. <ul><li>public class A implements Cloneable { </li></ul><ul><li>public HashMap map; </li></ul><ul><li>public A() { </li></ul><ul><li>map = new HashMap(); </li></ul><ul><li>map.put(&quot;key1&quot;, &quot;value1&quot;); </li></ul><ul><li>map.put(&quot;key2&quot;, &quot;value2&quot;); } </li></ul><ul><li>public Object clone() { </li></ul><ul><li>try { </li></ul><ul><li>A other = (A)super.clone(); </li></ul><ul><li>other.map = (HashMap)map.clone(); </li></ul><ul><li>return other; } catch (CloneNotSupportedException e) { </li></ul><ul><li>throw new InternalError(e.toString()); } </li></ul><ul><li>} } </li></ul>
  23. 23. Case 3: don’t use cloning <ul><li>Consider providing a copy constructor or a static factory method … </li></ul><ul><li>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); </li></ul><ul><li>A minor variant is to provide a static factory in place of a constructor: public static Yum newInstance(Yum yum); </li></ul>
  24. 24. Advantages over cloning… <ul><li>Cloneable/clone: They do not rely on a risk-prone extralinguistic object creation </li></ul><ul><li>they do not demand unenforceable adherence to ill-documented conventions; </li></ul><ul><li>they do not conflict with the proper use of final fields; they do not require the client to catch </li></ul><ul><li>an unnecessary checked exception ; and </li></ul><ul><li>they provide a statically typed object to the client. So type-casting </li></ul>
  25. 25. <ul><li>public StudioGroup(final Group pGroup) { </li></ul><ul><li>mId = pGroup.getId(); </li></ul><ul><li>setName(pGroup.getName()); </li></ul><ul><li>setDescription(pGroup.getName()); </li></ul><ul><li>Status status = pGroup.getStatus(); </li></ul><ul><li>setStatus(status == null ? null : new StudioStatus(status)); </li></ul><ul><li>List<Category> categories = pGroup.getCategories(); </li></ul><ul><li>setCategories(categories == null ? null : </li></ul><ul><li>new ArrayList<Category>(categories)); </li></ul><ul><li>setHotTopicsAccessible(pGroup.isHotTopicsAccessible()); </li></ul><ul><li>} </li></ul><ul><li>public Object clone() throws CloneNotSupportedException { </li></ul><ul><li>Group group = (Group) super.clone(); </li></ul><ul><li>Status status = group.getStatus(); </li></ul><ul><li>group.setStatus(status == null ? null : (Status) status.clone()); </li></ul><ul><li>List<Category> categories = group.getCategories(); </li></ul><ul><li>group.setCategories(categories == null ? null : (List<Category>) categories.clone()); </li></ul><ul><li>return group; </li></ul><ul><li>} </li></ul>
  26. 26. <ul><li>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. </li></ul><ul><li>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. </li></ul>
  27. 27. Thank You
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×