I try to implement immutable class, and I see a rule stating "Perform cloning of objects in the getter methods to return a copy rather than the returning actual object reference".
I understand that when we use immutables there would be no change in copied / cloned collections returned from the getters. When we use custom classes, the change in original collection can be seen also cloned ( shallow copied ) collection return from the getters.
In below code, I could not understand the case :
I created two methods, one for return the original collection as courseList and one for shallow copy of the courselist.
I assigned two version to local references clist1 and clist2.
Then I changed the item in original list. I can see the change original list and copied list also when I reach them through student object. However the change cannot be seen throug the reference I pointed to the cloned course list before ! I think it should also be affected by the change. Why I cant see the change on previously copied version ? This is reference and I think it should be point the same memory area, I also check the result by another example below again. I created a list containing StringBuilder. I appeded new strs to stringbuilder and then I can see the changed previously copied version of the list.
So, the main question, must I use the deep copy in immutable classes always ? Is this a wrong usage ? What would be the safe way to use collections in immutable classes ?
Thanks in advance.
ImmutableStudent.java
public final class ImmutableStudent {
public ImmutableStudent(String _name, Long _id, String _uni, ArrayList<String> _courseList){
name = _name;
id = _id;
uni = _uni;
courseList = new ArrayList<>();
_courseList.forEach( course -> courseList.add(course));
}
private final String name;
private final Long id;
private final String uni;
private final ArrayList<String> courseList;
public String getName() {
return name;
}
public Long getId() {
return id;
}
public String getUni() {
return uni;
}
public List<String> getCourseList() {
return courseList;
}
public List<String> getCourseListClone() {
return (ArrayList<String>)courseList.clone();
}
}
ImmutableHelper.java
public class ImmutableHelper {
public static void run(){
ArrayList<String> courseList = new ArrayList<>();
courseList.add("Literature");
courseList.add("Math");
String name = "Emma";
Long id = 123456L;
String uni = "MIT";
ImmutableStudent student = new ImmutableStudent(name, id, uni, courseList);
System.out.println(name == student.getName());
System.out.println(id.equals(student.getId()));
System.out.println(courseList == student.getCourseList());
System.out.println("Course List :" + student.getCourseList());
System.out.println("Course List Clone :" + student.getCourseListClone());
List<String> clist1 = student.getCourseList();
List<String> clist2 = student.getCourseListClone();
student.getCourseList().set(1, "Art");
System.out.println("Course List :" + student.getCourseList());
System.out.println("Course List Clone :" + student.getCourseListClone());
System.out.println("Previous Course List :" + clist1);
System.out.println("Previous Course List Clone :" + clist2);
// Check shallow copy using collections.clone()
ArrayList<StringBuilder> bList = new ArrayList<>();
StringBuilder a = new StringBuilder();
a.append("1").append("2").append("3");
StringBuilder b = new StringBuilder();
b.append("5").append("6").append("7");
bList.add(a);
bList.add(b);
ArrayList<StringBuilder> bListCp = (ArrayList<StringBuilder>)bList.clone();
System.out.println("Blist : " + bList);
System.out.println("BlistCp :" + bListCp);
a.append(4);
System.out.println("Blist : " + bList);
System.out.println("BlistCp :" + bListCp);
}
}
The Result
Course List :[Literature, Math]
Course List Clone :[Literature, Math]
Course List :[Literature, Math, Art]
Course List Clone :[Literature, Math, Art]
Previous Course List :[Literature, Math, Art]
Previous Course List Clone :[Literature, Math]
Blist : [123, 567]
BlistCp :[123, 567]
Blist : [1234, 567]
BlistCp :[1234, 567]