Java – use the copy constructor instead of object The correct method for clone deep replication
I have some code to use object Clone performs deep replication, but I'm trying to override it with a more "acceptable" replication constructor Here are two simple examples I'm trying to do. The first uses cloning and the second uses the copy constructor
Deep replication using clones
import java.util.*; abstract class Person implements Cloneable { String name; public Object clone() throws CloneNotSupportedException { return super.clone(); } } class Teacher extends Person implements Cloneable { int courses; public String toString() { return name + ": courses=" + courses; } } class Student extends Person implements Cloneable { double gpa; public String toString() { return name + ": gpa=" + gpa; } } public class DeepCopy_Clone { private static List<Person> deepCopy(List<Person> people) throws CloneNotSupportedException { List<Person> copy = new ArrayList<Person>(); for (Person person : people) { copy.add((Person)person.clone()); } return copy; } public static void main(String[] args) throws CloneNotSupportedException { ArrayList<Person> people = new ArrayList<Person>(); Teacher teacher = new Teacher(); teacher.name = "Teacher"; teacher.courses = 5; people.add(teacher); Student student = new Student(); student.name = "Student"; student.gpa = 4.0; people.add(student); List<Person> peopleCopy = deepCopy(people); // Invalidate the original data to prove a deep copy occurred teacher.name = null; teacher.courses = -1; student.name = null; student.gpa = -1; for (Person person : peopleCopy) { System.out.println(person.toString()); } } }
Deep copy using copy constructor
import java.util.*; abstract class Person { String name; public Person() {} public Person(Person other) { this.name = other.name; } public Person deepcopy() { if (this instanceof Teacher) { return new Teacher((Teacher)this); } else if (this instanceof Student) { return new Student((Student)this); } throw new Error("UnkNown type of person"); } } class Teacher extends Person { int courses; public Teacher() {} public Teacher(Teacher other) { super(other); this.courses = other.courses; } public String toString() { return name + ": courses=" + courses; } } class Student extends Person { double gpa; public Student() {} public Student(Student other) { super(other); this.gpa = other.gpa; } public String toString() { return name + ": gpa=" + gpa; } } public class DeepCopy_ConstructorAlternative { private static List<Person> deepCopy(List<Person> people) { List<Person> copy = new ArrayList<Person>(); for (Person person : people) { copy.add(person.deepcopy()); } return copy; } public static void main(String[] args) { ArrayList<Person> people = new ArrayList<Person>(); Teacher teacher = new Teacher(); teacher.name = "Teacher"; teacher.courses = 5; people.add(teacher); Student student = new Student(); student.name = "Student"; student.gpa = 4.0; people.add(student); List<Person> peopleCopy = deepCopy(people); // Invalidate the original data to prove a deep copy occurred teacher.name = null; teacher.courses = -1; student.name = null; student.gpa = -1; for (Person person : peopleCopy) { System.out.println(person.toString()); } } }
I find it interesting that despite all the evil discussion of cloning in Java, cloning alternatives require less code and less coercion (at least in this case)
I appreciate your feedback Will you do it in different ways? thank you.
Solution
Substitute:
public Object clone() throws CloneNotSupportedException { return super.clone(); }
I prefer:
public Person clone() { try { return (Person) clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException("This should be impossible ..."); } }
Therefore, the caller does not have to handle exceptions that will never happen, nor does he have to cast
In the copy construction method, type switching can better deal with polymorphism:
abstract class Person { ... public abstract Person deepcopy(); } class Student { ... public Student deepcopy() { return new Student(this); } } class Teacher { ... public Teacher deepcopy() { return new Teacher(this); } }
The compiler can now check that you provide a deep copy of all subtypes and that you do not need any casts
Finally, note that both clone and copy constructors have the same public API (it doesn't matter whether the method name is clone () or deepcopy ()), so the method you use is implementation details Copying the constructor is more verbose because you provide a constructor and a method to call it, but it can be more easily extended to the general type conversion tool, allowing the following:
public Teacher(Person p) { ... say("Yay,I got a job"); }
Suggestion: if you only need the same copy, use clone. If the caller may want to request an instance of a specific type, use copy constructors