clone()vs copy constructor vs factory方法?

Use*_*er1 78 java clone

我在Java上实现了克隆()的快速谷歌,发现:http: //www.javapractices.com/topic/TopicAction.do?Id = 71

它有以下评论:

复制构造函数和静态工厂方法提供了克隆的替代方法,并且更容易实现.

我想做的就是做一份深刻的副本.实现clone()似乎很有意义,但这篇谷歌排名很高的文章让我有点害怕.

以下是我注意到的问题:

复制构造函数不适用于泛型.

这是一些无法编译的伪代码.

public class MyClass<T>{
   ..
   public void copyData(T data){
       T copy=new T(data);//This isn't going to work.    
   }
   ..
}
Run Code Online (Sandbox Code Playgroud)

示例1:在泛型类中使用复制构造函数.

工厂方法没有标准名称.

拥有可重用代码的接口非常好.

public class MyClass<T>{
    ..
    public void copyData(T data){
        T copy=data.clone();//Throws an exception if the input was not cloneable
    }
    ..
}
Run Code Online (Sandbox Code Playgroud)

示例2:在泛型类中使用clone().

我注意到克隆不是静态方法,但是不是仍然需要制作所有受保护字段的深层副本吗?在实现clone()时,在非可克隆子类中抛出异常的额外工作对我来说似乎微不足道.

我错过了什么吗?任何见解将不胜感激.

Yis*_*hai 52

基本上,克隆被打破了.什么都不会轻易与泛型一起使用.如果你有这样的东西(缩短以获得重点):

public class SomeClass<T extends Copyable> {


    public T copy(T object) {
        return (T) object.copy();
    }
}

interface Copyable {
    Copyable copy();
}
Run Code Online (Sandbox Code Playgroud)

然后使用编译器警告您可以完成工作.因为泛型在运行时被擦除,所以执行复制的东西会在其中生成编译器警告生成强制转换.在这种情况下,这是不可避免的..在某些情况下可以避免(谢谢,kb304),但并非总体而言.考虑必须支持子类或实现接口的未知类的情况(例如,您正在迭代不一定生成相同类的可复制副本集合).

  • 克隆()上的Josh Bloch指针的+1 :-). (11认同)
  • 也可以在没有*编译器警告的情况下执行*. (2认同)

aka*_*okd 25

还有Builder模式.有关详细信息,请参阅Effective Java

我不明白你的评价.在复制构造函数中,您完全了解类型,为什么需要使用泛型?

public class C {
   public int value;
   public C() { }
   public C(C other) {
     value = other.value;
   }
}
Run Code Online (Sandbox Code Playgroud)

最近这里也有类似的问题.

public class G<T> {
   public T value;
   public G() { }
   public G(G<? extends T> other) {
     value = other.value;
   }
}
Run Code Online (Sandbox Code Playgroud)

一个可运行的样本:

public class GenTest {
    public interface Copyable<T> {
        T copy();
    }
    public static <T extends Copyable<T>> T copy(T object) {
        return object.copy();
    }
    public static class G<T> implements Copyable<G<T>> {
        public T value;
        public G() {
        }
        public G(G<? extends T> other) {
            value = other.value;
        }
        @Override
        public G<T> copy() {
            return new G<T>(this);
        }
    }
    public static void main(String[] args) {
        G<Integer> g = new G<Integer>();
        g.value = 1;
        G<Integer> f = g.copy();
        g.value = 2;
        G<Integer> h = copy(g);
        g.value = 3;
        System.out.printf("f: %s%n", f.value);
        System.out.printf("g: %s%n", g.value);
        System.out.printf("h: %s%n", h.value);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • (我知道这是旧的,但对后人来说)@CarlPritchett:这个错误将出现在Java 1.5及以下版本中.从Java 1.6开始,允许将接口方法标记为@ Override. (3认同)

Pet*_*rey 8

Java没有与C++相同的复制构造函数.

你可以有一个构造函数,它接受一个与参数类型相同的对象,但很少有类支持这个.(少于支持克隆的数量)

对于通用克隆,我有一个辅助方法,它创建一个类的新实例,并使用反射复制原始字段(浅拷贝)(实际上像反射但更快)

对于深层复制,一种简单的方法是序列化对象并对其进行反序列化.

顺便说一句:我的建议是使用不可变对象,然后你就不需要克隆它们了.;)

  • @Ievgen如果需要两次引用相同的数据,可以复制引用.您只需要复制对象的内容(如果它可能会更改)(但对于不知道的对象,您知道它不会) (2认同)

Nar*_*shi 7

以下是许多开发人员不使用的一些缺点Object.clone()

\n\n
    \n
  1. 使用Object.clone()方法需要我们在代码中添加大量语法,例如实现Cloneable接口、定义clone()方法和句柄CloneNotSupportedException,最后调用Object.clone()并强制转换为我们的对象。
  2. \n
  3. Cloneable接口缺少clone()方法,它是一个标记接口,并且其中没有任何方法,并且我们仍然需要实现它只是为了告诉 JVM 我们可以对clone()我们的对象执行。
  4. \n
  5. Object.clone()受到保护,因此我们必须提供我们自己的clone()间接调用Object.clone()
  6. \n
  7. 我们对对象构造没有任何控制权,因为Object.clone()它不会调用任何构造函数。
  8. \n
  9. clone()如果我们在子类中编写方法,例如Person,那么它的所有超类都应该clone()在其中定义方法或从另一个父类继承它,否则super.clone()链将失败。
  10. \n
  11. Object.clone()仅支持浅复制,因此新克隆对象的引用字段仍将保留原始对象的字段所持有的对象。为了克服这个问题,我们需要clone()在每个类中实现 who\xe2\x80\x99s 引用我们的类所持有的,然后在我们的clone()方法中单独克隆它们,如下例所示。
  12. \n
  13. 我们无法操作 Final 字段,Object.clone()因为 Final 字段只能通过构造函数更改。在我们的例子中,如果我们希望每个Person对象的 id 都是唯一的,那么如果我们使用,我们将得到重复的对象,Object.clone()因为Object.clone()不会调用构造函数,并且最终id字段可以\xe2\x80\x99t 修改Person.clone().
  14. \n
\n\n

复制构造函数比Object.clone()因为它们

\n\n
    \n
  1. Don\xe2\x80\x99t 强迫我们实现任何接口或抛出任何异常,但如果需要的话我们肯定可以做到。
  2. \n
  3. Don\xe2\x80\x99t 不需要任何强制转换。
  4. \n
  5. Don\xe2\x80\x99t 要求我们依赖于未知的对象创建机制。
  6. \n
  7. Don\xe2\x80\x99t 要求父类遵循任何契约或实现任何内容。
  8. \n
  9. 允许我们修改最终字段。
  10. \n
  11. 允许我们完全控制对象的创建,我们可以在其中编写初始化逻辑。
  12. \n
\n\n

阅读有关Java 克隆 - 复制构造函数与克隆的更多信息

\n


Ale*_*pac 6

我认为Yishai的答案可以改进,所以我们不能用以下代码发出警告:

public class SomeClass<T extends Copyable<T>> {

    public T copy(T object) {
        return object.copy();
    }
}

interface Copyable<T> {
    T copy();
}
Run Code Online (Sandbox Code Playgroud)

这样一个需要实现Copyable接口的类必须是这样的:

public class MyClass implements Copyable<MyClass> {

    @Override
    public MyClass copy() {
        // copy implementation
        ...
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 几乎.Copyable接口应该声明为:`interface Copyable <T extends Copyable>`,这是你可以用Java编码的自我类型最接近的东西. (2认同)