2010-11-16 24 views
7

yerine copy constructor ile doğru bir şekilde kopyalamak için doğru yolu Object.clone kullanarak bir derin kopya gerçekleştiren bir kod var, ancak daha "kabul edilebilir" bir kopya oluşturucu tekniği kullanarak yeniden yazmaya çalışıyorum. Aşağıda, yapmaya çalıştığım şeyin iki basit örneği, ilk klon kullanımı ve ikincisi bir kopya kurucuyu kullanarak.Object.clone

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

Derin kopya kullanarak kopya yapıcısı

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

Benim ilginç bulduğum olduğunu Java klonlama kötülükleri hakkında tüm ifadesine rağmen, Klonlama alternatifi daha az kod ve daha az döküm gerektirir (bu durumda, en azından).

Kopya oluşturucu alternatifiyle ilgili geri bildirimleri takdir ediyorum. Bunu başka bir şekilde yapar mısın? Teşekkürler.

cevap

3

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

böylece arayanlar meydana asla ve döküm gerekmez istisna işlemek gerekmiyor.

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

şimdi derleyici tüm alt tipleri için derin bir kopyasını sağladı kontrol edebilirsiniz ve herhangi atmalarını gerekmez: copy-yapıcı yaklaşımda

, tip anahtarlama iyi polymorphically ele alınır.

Son olarak, klonlama ve kopyalama-yapıcı yaklaşımı hem (yöntem clone() veya deepCopy() çok önemli değil denir olsun) aynı kamu api sahip olduğunu, dolayısıyla hangi yaklaşım kullandığınız bir uygulama ayrıntıdır. kopyalamaya karşı yapıcı yaklaşım bir kurucu ve kurucu çağırmak bir yöntem sağlamak hem daha ayrıntılı, ancak daha kolay gibi şeyler sağlayan bir genel tür dönüştürme tesisine genelleştirilebilir:

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

Tavsiye: Kullanım klonu Aynı kopyayı sadece istiyorsanız, arayanın belirli bir türden bir örnek isteyebilirse kopya-kurucuları kullanın.

1

Kopya oluşturucu yaklaşımının Person.deepCopy sınıfında, Person sınıfının tüm alt sınıflarını açıkça test etmesi gerektiğini unutmayın. Bu temel bir tasarım, kod bakım ve test sorunudur: Birisi Person'un yeni bir alt sınıfını tanıtırsa, Person.deepCopy'u unutmadan veya güncelleyemediğinde başarılı klonlamayı engeller. .clone() yöntemi, sanal bir yöntem sağlayarak bu sorunu önler (clone).

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

tercih ediyorum: Yerine

+2

Person.deepCopy'yi unutmak veya güncelleyememek, karşılaştırmaya göre sorun değil. Yani, klon alternatifi ile çok benzer sorunlarla karşılaşabilirsiniz. Örneğin, bir kişi yeni bir "sınıf Yöneticisi Kişiyi uzatır" yaratırsa, Yönetici.clone'u uygulamayı hatırlamak zorunda kalacaktır (alandaki bir kopya kopyayı derin bir kopya yapmadığı varsayılarak). Person.deepCopy'yi güncelleyememek, alt sınıfta geçersiz kılarak ele alınabilir. Ve evet, bunu yapmayı hatırlamak zorundasınız, ama yine aynı klon alternatifi ile aynı sorun. – vocaro

1

Klon tabanlı bir yaklaşımın avantajlarından biri, düzgün bir şekilde uygulandığında, klonlandığında özel davranış gerektirmeyen türetilmiş türlerin özel klonlama kodu gerektirmeyeceğidir. Bu arada, bir klonlama yöntemini ortaya çıkaran sınıfların genellikle kalıtsal olmaması gerektiğini düşünmeye eğilimliyim; bunun yerine bir temel sınıf, korunan bir yöntem olarak klonlamayı desteklemeli ve türetilmiş bir sınıf, bir arabirim aracılığıyla klonlamayı desteklemelidir.Bir nesne klonlamayı desteklemiyorsa, bir Klonlama API'sından bir istisna atmamalı; bunun yerine, nesnenin bir klon API'sı OLMAMALIDIR.