我有一些使用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中的弊端的讨论,但克隆替代方案需要更少的代码和更少的演员阵容(至少在这种特殊情况下)。

我会感谢复制构造函数替代方案的反馈。你会做什么不同吗?谢谢。

有帮助吗?

解决方案

代替:

 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");
}

建议:如果仅想要相同的副本,请使用克隆,如果您的呼叫者可能希望请求特定类型的实例,请使用复制构造器。

其他提示

请注意,在 Person.deepCopy 复制构造方法的方法 Person 课程必须明确测试其所有子类。这是一个基本设计,代码维护和测试问题:如果有人引入了新的子类,它将阻止克隆成功 Person, ,忘记或无法更新 Person.deepCopy. 。这 .clone() 方法通过提供虚拟方法来避免此问题(clone).

基于克隆的方法的一个优点是,如果正确实现,则在克隆时不需要特殊行为的派生类型不需要特殊的克隆代码。顺便说一句,我倾向于认为暴露克隆方法的类通常不应该遗传。取而代之的是,基类应作为受保护的方法支持克隆,而派生类应通过接口支持克隆。如果对象不支持克隆,则不应从克隆API中引发异常。相反,对象不应具有克隆API。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top