For a class with an array field Josh says if the clone method merely return super.clone(), the resulting class instance will have the correct values in primitive fields, but its array field will refer to the same array as the original class instance. Modifying the original will destroy the invariants and vice-versa.
He used the example of custom Stack implementation, I am using a simple Student class
class Student implements Cloneable {
private String name;
private int age;
private int[] marks = {90, 70, 80};
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setMarks(int[] marks) {
this.marks = marks;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
@Override
public String toString() {
return "Student - Name : " + name + " Age : " + age + " Marks : " + Arrays.toString(marks);
}
}
Please note: I didn't invoke clone() on my array field in my clone method's override.
Then I did:
public class CloningDemo {
public static void main(String[] args) {
Student s1 = new Student("Mohit", 30);
Student s2 = null;
try {
s2 = s1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println("S1 : " + s1);
System.out.println("S2 : " + s2);
System.out.println("Updating the clone...");
s2.setName("Rohit");
s2.setAge(29);
s2.setMarks(new int[]{10, 29, 30});
System.out.println("S1 : " + s1);
System.out.println("S2 : " + s2);
System.out.println("Updating the array elements in Original...");
s1.setMarks(new int[]{10, 10, 10});
System.out.println("S1 : " + s1);
System.out.println("S2 : " + s2);
}
}
Output:
S1 : Student - Name : Mohit Age : 30 Marks : [90, 70, 80]
S2 : Student - Name : Mohit Age : 30 Marks : [90, 70, 80]
Updating the clone...
S1 : Student - Name : Mohit Age : 30 Marks : [90, 70, 80]
S2 : Student - Name : Rohit Age : 29 Marks : [10, 29, 30]
Updating the array elements in Original...
S1 : Student - Name : Mohit Age : 30 Marks : [10, 10, 10]
S2 : Student - Name : Rohit Age : 29 Marks : [10, 29, 30]
I was wondering that changing array in original instance would change the array in my clone too, because I mentioned above "array field will refer to the same array as the original instance"
With my implementation of clone I should have seeing changes in the clone s2 too. The proper implementation would've been:
@Override
protected Student clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
student.marks = marks.clone(); // I am not doing this in my code.
return student;
}
Have I misunderstood this? Can someone please explain what is going on?
Thanks
~Mohit
A variable of type
int[]
may be used to encapsulate any of four different things:The contents of an array which will never be modified.
The contents of an array which might be modified, and to which no references exist that the owner of the variable doesn't know about.
The identity of an array which might be modified, and which is owned by someone else.
The identity of an array which might be modified, and which is owned by the owner of the variable, but to which other references may exist.
A
clone()
method doesn't need to clone arrays of the first type, but aside from a slight performance cost cloning such arrays is likely to be harmless. Aclone()
method must, however, clone arrays of the second type and refrain from cloning arrays of the third type. Objects that own arrays of the fourth type should generally not implementclone()
.It's not clear whether you really want your code to consider the array to be of the first type or the third; in neither case is it necessary for your
clone
method to clone the array. The second pattern is the most common when using array-type variables, but your particular usage case doesn't fit it.For every array-type variable, identify which of the four cases applies and it will be clear how you should proceed with
clone
. Note that you can't classify an array into one of the four types, your code is probably broken, and you should fix it before worrying aboutclone
.By calling
s1.setMarks(new int[]{10, 10, 10});
you're creating a completely new array and write its reference to the variablemarks
ofs1
. Sos1
ands2
refer to two different arrays.If you would have this method:
in the class
Student
and perform following code:then you will see, that this affects
s2
too:(don't forget to comment the line
s2.setMarks(new int[]{10, 29, 30});
, as well, because this line also creates a new array reference and removes the (array) binding betweens1
ands2
)This behavior could be described with a "real world example":
Image you and a friend are holding a rope, with one person on each end. This rope represents the
Array
you're both referring to. If your friend pulls that rope (changing a value in that array), you'll notice that. And if you pull that rope, your friend will notice that, too.By calling
s1.setMarks(new int[]{...});
your friend gets a new rope and he will drop the first one for it. If he pulls that rope, you won't notice that, because you two have different ones. By callings2.setMarks(new int[]{...});
you will get a new rope and drop the first one, as well. This is the signal for the third friend, calledGarbage Collector
, to take that rope and dump it, because no one is using it anymore. But this friend is kind of lazy, so there is no guarantee that he will do that immediately.