使用复制构造函数而不是 Object.clone 进行深度复制的正确方法
我有一些使用 Object.clone 执行深层复制的代码,但我正在尝试使用更“可接受的”复制构造函数技术重写它。下面是我想要做的两个简单示例,第一个使用克隆,第二个使用复制构造函数。
使用克隆进行深度复制
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.*;
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());
}
}
}
我发现有趣的是,尽管大家都在谈论 Java 中克隆的弊端,但克隆替代方案需要更少的代码和更少的转换(至少在这个特殊情况下)。
我希望得到有关复制构造函数替代方案的反馈。你会采取不同的做法吗?谢谢。
I have some code that performs a deep copy using Object.clone, but I'm trying to rewrite it using the more "acceptable" copy constructor technique. Below are two simple examples of what I'm trying to do, the first using clone and the second using a copy constructor.
Deep copy using clone
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());
}
}
}
What I find interesting is that despite all the talk about the evils of cloning in Java, the clone alternative requires less code and fewer casts (in this particular case, at least).
I'd appreciate feedback on the copy constructor alternative. Would you do it any differently? Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
而不是:
我更喜欢:
这样调用者就不必处理永远不会发生的异常,也不必进行强制转换。
在复制构造函数方法中,可以更好地以多态方式处理类型切换:
现在编译器可以检查您是否为所有子类型提供了深层复制,并且不需要任何强制转换。
最后,请注意,克隆和复制构造函数方法具有相同的公共 api(该方法是调用
clone()
还是deepCopy()
并不重要) ,因此您使用哪种方法是实现细节。复制构造函数方法更为冗长,因为您提供了构造函数和调用该构造函数的方法,但它可以更轻松地推广为通用类型转换工具,允许执行以下操作:建议:如果您只需要相同的副本,请使用克隆,如果您的调用者可能希望请求特定类型的实例,请使用复制构造函数。
Instead of:
I'd prefer:
so callers don't have to handle an exception that can never occur, and don't have to cast.
In the copy-constructor approach, the type switching is better handled polymorphically:
now the compiler can check that you have provided deep copy for all subtypes, and you don't need any casts.
Finally, note that both the cloning and copy-constructor approach have the same public api (whether the method is called
clone()
ordeepCopy()
doesn't matter much), so which approach you use is an implementation detail. The copy-constructor approach is more verbose as you provide both a constructor and a method calling that constructor, but it can be more easily generalized to a general type conversion facility, allowing things like:Recommendation: Use clone if you only want an identical copy, use copy-constructors if your caller might wish to request an instance of a specific type.
请注意,在复制构造函数方法的
Person.deepCopy
中,Person
类必须显式测试其所有子类。这是一个基本的设计、代码维护和测试问题:如果有人引入Person
的新子类、忘记或无法更新Person.deepCopy
,则会阻止成功克隆。.clone()
方法通过提供虚拟方法 (clone
) 来避免此问题。Please note that in
Person.deepCopy
of the copy constructor approach, thePerson
class has to test for all its subclasses explicitly. This is a fundamental design, code maintenance and testing issue: it would prevent successful cloning if someone introduces a new subclass ofPerson
, forgetting or being unable to updatePerson.deepCopy
. The.clone()
method avoids this problem by providing a virtual method (clone
).基于克隆的方法的优点之一是,如果实现得当,在克隆时本身不需要特殊行为的派生类型将不需要特殊的克隆代码。顺便说一句,我倾向于认为公开克隆方法的类通常不应该是可继承的;相反,基类应该支持克隆作为受保护的方法,而派生类应该支持通过接口进行克隆。如果一个对象不支持克隆,它不应该从 Clone API 抛出异常;相反,该对象不应该有克隆 API。
One advantage of a clone-based approach is that, if properly implemented, derived types which do not themselves require special behavior when cloned won't require special cloning code. Incidentally, I tend to think that classes which expose a cloning method should generally not be inheritable; instead, a base class should supports cloning as a protected method, and a derived class should support cloning via an interface. If an object won't support cloning, it shouldn't throw an exception from a Clone API; instead, the object shouldn't HAVE a clone API.