Java 使用复制构造函数而不是Object.clone进行深度复制的正确方法
我有一些代码使用Object.clone执行深度复制,但我正在尝试使用更“可接受”的复制构造函数技术重写它。下面是我尝试做的两个简单示例,第一个使用克隆,第二个使用复制构造函数 使用克隆进行深度复制Java 使用复制构造函数而不是Object.clone进行深度复制的正确方法,java,clone,copy-constructor,deep-copy,cloneable,Java,Clone,Copy Constructor,Deep Copy,Cloneable,我有一些代码使用Object.clone执行深度复制,但我正在尝试使用更“可接受”的复制构造函数技术重写它。下面是我尝试做的两个简单示例,第一个使用克隆,第二个使用复制构造函数 使用克隆进行深度复制 import java.util.*; abstract class Person implements Cloneable { String name; public Object clone() throws CloneNotSupportedException {
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());
}
}
}
import java.util.*;
抽象类Person实现了Cloneable{
字符串名;
公共对象克隆()引发CloneNotSupportedException{
返回super.clone();
}
}
班主任延伸人实现可克隆性{
int课程;
公共字符串toString(){return name+“:courses=“+courses;”
}
类学生扩展人实现可克隆{
双gpa;
公共字符串toString(){return name+“:gpa=“+gpa;”
}
公共类DeepCopy\u克隆{
私有静态列表deepCopy(列表人员)抛出CloneNotSupportedException{
列表副本=新建ArrayList();
用于(人:人){
copy.add((Person)Person.clone());
}
返回副本;
}
公共静态void main(字符串[]args)引发CloneNotSupportedException{
ArrayList people=新建ArrayList();
教师=新教师();
teacher.name=“teacher”;
教师课程=5;
添加(教师);
学生=新生();
student.name=“student”;
学生平均成绩=4.0;
添加(学生);
List peopleCopy=deepCopy(人);
//使原始数据无效,以证明发生了深度复制
teacher.name=null;
教师课程=-1;
student.name=null;
学生平均成绩=-1;
用于(个人:个人副本){
System.out.println(person.toString());
}
}
}
使用复制构造函数进行深度复制
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());
}
}
}
import java.util.*;
抽象类人{
字符串名;
公众人物(){}
公众人士(其他人士){
this.name=other.name;
}
公众人士{
如果(教师的这个实例){
返回新教师((教师)本);
}else if(学生的此实例){
返回新学生((学生)本);
}
抛出新错误(“未知类型的人”);
}
}
班主任延伸人{
int课程;
公共教师(){}
公共教师(其他教师){
超级(其他);
本课程=其他课程;
}
公共字符串toString(){return name+“:courses=“+courses;”
}
班级学生延伸人{
双gpa;
公立学生(){}
公立学生(其他学生){
超级(其他);
this.gpa=other.gpa;
}
公共字符串toString(){return name+“:gpa=“+gpa;”
}
公共类DeepCopy\u构造函数替代{
私有静态列表deepCopy(列出人员){
列表副本=新建ArrayList();
用于(人:人){
copy.add(person.deepCopy());
}
返回副本;
}
公共静态void main(字符串[]args){
ArrayList people=新建ArrayList();
教师=新教师();
teacher.name=“teacher”;
教师课程=5;
添加(教师);
学生=新生();
student.name=“student”;
学生平均成绩=4.0;
添加(学生);
List peopleCopy=deepCopy(人);
//使原始数据无效,以证明发生了深度复制
teacher.name=null;
教师课程=-1;
student.name=null;
学生平均成绩=-1;
用于(个人:个人副本){
System.out.println(person.toString());
}
}
}
我发现有趣的是,尽管人们都在谈论Java中克隆的坏处,但克隆替代方案需要更少的代码和更少的强制转换(至少在这种情况下)
我希望得到关于复制构造函数替代方案的反馈。你会有不同的做法吗?谢谢。请注意,在复制构造函数方法的
Person.deepCopy
中,Person
类必须显式测试其所有子类。这是一个基本的设计、代码维护和测试问题:如果有人引入了Person
的新子类,忘记或无法更新Person.deepCopy
,将阻止成功克隆。.clone()
方法通过提供虚拟方法(clone
)来避免此问题。而不是:
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
我更喜欢:
public Person clone() {
try {
return (Person) clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("This should be impossible ...");
}
}
因此,调用方不必处理永远不会发生的异常,也不必强制转换
在复制构造函数方法中,类型切换在多态性方面处理得更好:
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);
}
}
现在,编译器可以检查您是否为所有子类型提供了深度副本,并且您不需要任何强制转换
最后,请注意,克隆和复制构造函数方法都具有相同的公共api(该方法是否被称为clone()
或deepCopy()
并不重要),因此使用哪种方法是一个实现细节。复制构造函数方法更为详细,因为您同时提供了构造函数和调用该构造函数的方法,但它可以更容易地推广到通用类型转换工具,允许如下操作:
public Teacher(Person p) {
...
say("Yay, I got a job");
}
建议:如果只需要相同的副本,请使用克隆;如果调用方可能希望请求特定类型的实例,请使用复制构造函数。基于克隆的方法的一个优点是,如果正确实现,克隆时本身不需要特殊行为的派生类型将不需要特殊的克隆代码。顺便说一句,我倾向于认为公开克隆方法的类通常不应该是可继承的;相反,基类应该支持