By
convention, the approach of writing clone method is:
1.
Implements Cloneable interface
This
approach ensures your clone method can directly or indirectly call
Object.clone(). Otherwise, calling Object.clone() will throws
CloneNotSupportedException. Why we need to call Object.clone() in our
clone method? Please see approach 2.2.
2. Override the clone
method
2.1 Make the clone method to public method
Please
be noted that the clone method type of Object class is:
protected
Object clone() throws CloneNotSupportedException
In order to
support other class can use our clone method, we should define it as
public method.
2.2 Call super.clone() to produce the new
object
By convention, the object returned by clone method
should be obtained by calling super.clone (this means it’s better
to produce the new object by super.clone() than directly use “new”
operator). If a class and all of its superclasses (except Object)
obey this convention, it will be the case that x.clone().getClass()
== x.getClass().
Key point: why we should use super.clone() to
produce the new object instead of directly use “new”
operator?
First of all, if all classes obey this convention,
our clone method will directly or indirectly call Object.clone
method. This method is a native method, it will be more efficient
than directly “new” an object.
Secondly, Object.clone
method can recognize the class type which called the clone method
using RTTI mechanism. And it will return the new object which has the
correct class type.
For example:
1 class A implements Cloneable
2 class B extends A implements Cloneable {
3
4 public Object clone() throws CloneNotSupportedException {
5 B b = null;
6
7 b = (B) super.clone(); // It seems that super.clone() is
8 // A.clone(), so it will return an
9 // object of Class A. This is incorrect.
10 // If the clone method of class A calls
11 // super.clone method too, it will
12 // return a new object belongs to
13 // class B. Thus, we can cast it to
14 // class B. This is the benefit of
15 // Object.clone().
16 return b;
17 }
18 }
Now,
let’s consider another case, if we write clone method of class A
like this:
1 class A {
2 public Object clone() {
3 A a = null;
4 a = new A();
5 // Then do some copy data operation.
6 return a;
7 }
8 }
When B.clone() calls super.clone(),unfortunately we can only get
the object whose class is A. And we can’t cast the new object to
class B since B is a subclass of A.
That’s why it’s
strongly recommended that clone method of all classes obey the
convention that obtained the new object by calling super.clone().
2.3 Clone members
There are two cases: If the member supports
clone, it’s better to call the clone method of the member to return
a copy object of this member. If the member doesn’t support clone,
you should create a new object which is the copy of the member. After
this approach, it will be ensured that x.clone.equals(x) and
x.clone() is independent with x.
Examples:
1 /**
2 * class B support clone
3 *
4 */
5 class B implements Cloneable {
6 private int intMember;
7
8 public B(int i) {
9 intMember = i;
10 }
11
12 public void setIntMember(int i) {
13 intMember = i;
14 }
15
16 public Object clone() throws CloneNotSupportedException {
17 B clonedObject = null;
18
19 // Firstly, call super.clone to return new object
20 clonedObject = (B) super.clone();
21
22 // Secondly, clone member here
23 clonedObject.setIntMember(intMember);
24
25 // The end, return new object
26 return clonedObject;
27 }
28 }
29
30 /**
31 * class C doesn't support clone
32 *
33 */
34 class C {
35 private int intMember;
36
37 public C(int i) {
38 intMember = i;
39 }
40
41 public void setIntMember(int i) {
42 intMember = i;
43 }
44
45 public int getIntMember() {
46 return intMember;
47 }
48 }
49
50 class A implements Cloneable {
51 private int intMember = 0;
52 private String stringMember = "";
53 private B supportCloneMember = null;
54 private C notSupportCloneMember = null;
55
56 public void setIntMember(int i) {
57 intMember = i;
58 }
59
60 public void setStringMember(String s) {
61 stringMember = s;
62 }
63
64 public void setB(B b) {
65 supportCloneMember = b;
66 }
67
68 public void setC(C c) {
69 notSupportCloneMember = c;
70 }
71
72 public Object clone() throws CloneNotSupportedException {
73 A clonedObject = null;
74
75 // Firstly, call super.clone to return new object
76 clonedObject = (A) super.clone();
77
78 // Secondly, clone members here
79
80 // For basic type member, directly set it to clonedObject
81 // Because basic type parameter passes value. Modify
82 // clonedObject.intMember can not effect the intMember
83 // of itself.
84 clonedObject.setIntMember(intMember);
85 // For immutable member, directly set it to clonedObject.
86 // Becasue we can not change the value of immutable
87 // variable once it was setted.
88 clonedObject.setStringMember(stringMember);
89 // For member which support clone, we just clone it and
90 // set the return object to the member of new object.
91 B clonedB = (B) supportCloneMember.clone();
92 clonedObject.setB(clonedB);
93 // For member which do not support clone, we need to create
94 // new object.
95 C clonedC = new C(notSupportCloneMember.getIntMember());
96 clonedObject.setC(clonedC);
97
98 // The end, return new object
99 return clonedObject;
100 }
101 }