Frage

Ich habe einige Codes, führt eine tiefe Kopie mit Object.clone, aber ich versuche, es neu zu schreiben, die mehr „akzeptabel“ Copykonstruktor Technik. Im Folgenden sind zwei einfache Beispiele dafür, was ich versuche, die erste mit Klon und die zweite eine Kopie Konstruktor.

zu tun,

Deep kopieren Sie mit Klon

 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 kopieren Sie mit Copykonstruktor

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

Was ich interessant finde ist, dass trotz all des Geredes über das Übel der in Java Klonen, die Klon Alternative weniger Code und wenige Würfe erfordert (in diesem speziellen Fall, zumindest).

würde ich Feedback über die Copykonstruktor Alternative zu schätzen wissen. Würden Sie es tun anders? Danke.

War es hilfreich?

Lösung

Statt:

 public Object clone() throws CloneNotSupportedException {
     return super.clone();
 }

Ich würde es vorziehen:

public Person clone() {
    try {
        return (Person) clone();
    } catch (CloneNotSupportedException e) {
        throw new RuntimeException("This should be impossible ...");
    }
}

so Anrufer müssen keine Ausnahme behandeln, die nie auftreten können, und haben nicht zu werfen.

In dem Copy-Konstruktor Ansatz wird der Typ-Schalt polymorph besser behandelt:

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

Jetzt kann der Compiler überprüfen, ob Sie für alle Subtypen tiefe Kopie zur Verfügung gestellt haben, und Sie brauchen keine Würfe.

Schließlich ist zu beachten, dass sowohl das Klonen und Copy-Konstruktor Ansatz die gleiche öffentliche api haben (ob die Methode clone() oder deepCopy() genannt wird, spielt keine große Rolle), so die Sie nähern Verwendung ist eine Implementierung Detail. Der Copy-Konstruktor Ansatz ist ausführlicher, wie Sie beide einen Konstruktor und ein Verfahren bereitzustellen, dass Konstruktor aufrufe, aber es kann leichter zu einer allgemeinen Typ-Umwandlungsanlage verallgemeinert werden, so dass Dinge wie:

public Teacher(Person p) {
    ...
    say("Yay, I got a job");
}

. Empfehlung: Verwenden Klon, wenn Sie nur eine identische Kopie möchten, verwenden Sie copy-Konstrukteuren, wenn Ihr Anrufer könnte wünschen eine Instanz eines bestimmten Typs beantragen

Andere Tipps

Bitte beachten Sie, dass in Person.deepCopy des Copykonstruktor Ansatz hat die Person Klasse Test für alle ihre Unterklassen ausdrücklich. Dies ist eine grundlegende Design, Code Wartung und Prüfung Ausgabe: es erfolgreiche Klonen verhindern würde, wenn jemand stellt eine neue Unterklasse von Person, zu vergessen, oder nicht in der Lage sein zu aktualisieren Person.deepCopy. Das .clone() Verfahren vermeidet dieses Problem durch eine virtuelle Methode bereitstellt (clone).

Ein Vorteil eines Klon-basierten Ansatzes ist, dass, wenn sie richtig umgesetzt werden, abgeleitete Typen, die sich nicht besonderes Verhalten erfordern, wenn geklont erfordert keine speziellen Cloning-Code. Im Übrigen neige ich dazu, zu denken, dass Klassen, die eine Klonierungsverfahren im Allgemeinen nicht vererbbar sein aussetzen sollte; Stattdessen sollte eine Basisklasse Klonieren Abstützungen als geschützte Methode und eine abgeleitete Klasse sollte die Klonierung über eine Schnittstelle unterstützen. Wenn ein Objekt nicht das Klonen unterstützen wird, soll es nicht eine Ausnahme von einem Klon API werfen; stattdessen sollte das Objekt nicht um einen Klon-API.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top