7

Object.clone を使用してディープ コピーを実行するコードがいくつかありますが、より「受け入れられる」コピー コンストラクター手法を使用してコードを書き直そうとしています。以下は、私がやろうとしていることの 2 つの簡単な例です。最初はクローンを使用し、2 番目はコピー コンストラクターを使用しています。

クローンを使用したディープ コピー

 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 でのクローン作成の弊害についてのあらゆる議論にもかかわらず、クローンの代替案はより少ないコードとより少ないキャスト (少なくともこの特定のケースでは) を必要とするということです。

コピー コンストラクターの代替案に関するフィードバックをいただければ幸いです。あなたはそれを別の方法で行いますか?ありがとう。

4

3 に答える 3

3

それ以外の:

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

推奨事項: 同一のコピーのみが必要な場合は clone を使用し、呼び出し元が特定の型のインスタンスを要求する可能性がある場合は copy-constructors を使用します。

于 2010-11-16T22:21:09.950 に答える
1

クローンベースのアプローチの利点の 1 つは、適切に実装されていれば、クローンされたときに特別な動作を必要としない派生型は、特別なクローン コードを必要としないことです。ちなみに、クローン メソッドを公開するクラスは一般に継承可能であってはならないと考える傾向があります。代わりに、基本クラスは保護されたメソッドとして複製をサポートする必要があり、派生クラスはインターフェイスを介して複製をサポートする必要があります。オブジェクトが複製をサポートしない場合、複製 API から例外をスローすべきではありません。代わりに、オブジェクトはクローン API を持つべきではありません。

于 2010-11-22T23:26:27.137 に答える
1

Person.deepCopyコピー コンストラクターのアプローチでは、Personクラスはすべてのサブクラスを明示的にテストする必要があることに注意してください。Personこれは、基本的な設計、コードのメンテナンス、およびテストの問題です。誰かが の新しいサブクラスを導入したり、 を忘れたり、更新できない場合、複製の成功が妨げられPerson.deepCopyます。メソッドは.clone()、仮想メソッド ( clone) を提供することで、この問題を回避します。

于 2010-11-16T22:03:41.290 に答える