Java clone() vs 复制构造函数 vs 工厂方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1106102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
clone() vs copy constructor vs factory method?
提问by User1
I did a quick google on implementing clone() in Java and found: http://www.javapractices.com/topic/TopicAction.do?Id=71
我做了一个关于在 Java 中实现 clone() 的快速谷歌,发现:http: //www.javapractices.com/topic/TopicAction.do?Id=71
It has the following comment:
它有以下评论:
copy constructors and static factory methods provide an alternative to clone, and are much easier to implement.
复制构造函数和静态工厂方法提供了克隆的替代方法,并且更容易实现。
All I want to do is make a deep copy. Implementing clone() seems to make a lot of sense, but this highly google ranked article makes me a bit afraid.
我想做的就是做一个深拷贝。实现 clone() 似乎很有意义,但是这篇谷歌排名很高的文章让我有点害怕。
Here are the issues that I've noticed:
以下是我注意到的问题:
Copy constructors don't work with Generics.
复制构造函数不适用于泛型。
Here's some pseudo-code that won't compile.
这是一些无法编译的伪代码。
public class MyClass<T>{
..
public void copyData(T data){
T copy=new T(data);//This isn't going to work.
}
..
}
Sample 1: Using a copy constructor in a generic class.
示例 1:在泛型类中使用复制构造函数。
Factory methods don't have standard names.
工厂方法没有标准名称。
It's quite nice to have an interface for reusable code.
有一个可重用代码的接口是非常好的。
public class MyClass<T>{
..
public void copyData(T data){
T copy=data.clone();//Throws an exception if the input was not cloneable
}
..
}
Sample 2: Using clone() in a generic class.
示例 2:在泛型类中使用 clone()。
I noticed that clone is not a static method, but wouldn't it still be necessary to make deep copies of all the protected fields? When implementing clone(), the extra effort to throw exceptions in non-cloneable subclasses seems trivial to me.
我注意到 clone 不是一个静态方法,但是否仍然需要对所有受保护的字段进行深度复制?在实现 clone() 时,在不可克隆的子类中抛出异常的额外努力对我来说似乎微不足道。
Am I missing something? Any insights would be appreciated.
我错过了什么吗?任何见解将不胜感激。
采纳答案by Yishai
Basically, clone is broken. Nothing will work with generics easily. If you have something like this (shortened to get the point across):
基本上,克隆已损坏。没有什么可以轻松地使用泛型。如果你有这样的东西(缩短以理解这一点):
public class SomeClass<T extends Copyable> {
public T copy(T object) {
return (T) object.copy();
}
}
interface Copyable {
Copyable copy();
}
Then with a compiler warning you can get the job done. Because generics are erased at runtime, something that does a copy is going to have a compiler warning generating cast in it. It is not avoidable in this case.. It is avoidable in some cases (thanks, kb304) but not in all. Consider the case where you have to support a subclass or an unknown class implementing the interface (such as you were iterating through a collection of copyables that didn't necessarily generate the same class).
然后通过编译器警告您可以完成工作。因为泛型在运行时被删除,所以复制的东西将有一个编译器警告在其中生成强制转换。在这种情况下是无法避免的。. 在某些情况下是可以避免的(谢谢,kb304),但并非在所有情况下都可以避免。考虑您必须支持实现接口的子类或未知类的情况(例如,您正在迭代一组不一定生成相同类的可复制对象)。
回答by Peter Lawrey
Java doesn't have copy constructors in the same sense that C++ does.
Java 没有与 C++ 相同的复制构造函数。
You can have a constructor which takes an object of the same type as an argument, but few classes support this. (less than the number which support clone able)
你可以有一个构造函数,它接受一个相同类型的对象作为参数,但很少有类支持这个。(小于支持克隆的数量)
For a generic clone I have a helper method which creates a new instance of a class and copies the fields from the original (a shallow copy) using reflections (actually something like reflections but faster)
对于通用克隆,我有一个辅助方法,它创建一个类的新实例并使用反射从原始(浅拷贝)复制字段(实际上类似于反射但速度更快)
For a deep copy, a simple approach is to serialize the object and de-serialize it.
对于深拷贝,一个简单的方法是序列化对象并反序列化它。
BTW: My suggest is to use immutable objects, then you won't need to clone them. ;)
顺便说一句:我的建议是使用不可变对象,然后你就不需要克隆它们了。;)
回答by akarnokd
There is also the Builder pattern. See Effective Java for details.
还有建造者模式。有关详细信息,请参阅有效 Java。
I don't understand your evaluation. In a copy constructor you are fully aware of the type, why is there a need to use generics?
我不明白你的评价。在复制构造函数中,您完全了解类型,为什么需要使用泛型?
public class C {
public int value;
public C() { }
public C(C other) {
value = other.value;
}
}
There was a similar question recently here.
最近这里有一个类似的问题。
public class G<T> {
public T value;
public G() { }
public G(G<? extends T> other) {
value = other.value;
}
}
A runnable sample:
一个可运行的示例:
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);
}
}
回答by Martin v. L?wis
What you are missing is that clone creates shallow copies by default and convention, and that making it create deep copies is, in general, not feasible.
您缺少的是克隆默认和约定创建浅拷贝,并且使其创建深拷贝通常是不可行的。
The problem is that you cannot really create deep copies of cyclic object graphs, without being able to keep track what objects have been visited. clone() does not provide such tracking (as that would have to be a parameter to .clone()), and thus only creates shallow copies.
问题是你不能真正创建循环对象图的深层副本,而不能跟踪哪些对象已经被访问过。clone() 不提供这样的跟踪(因为它必须是 .clone() 的参数),因此只创建浅拷贝。
Even if your own object invokes .clone for all of its members, it still won't be a deep copy.
即使您自己的对象为其所有成员调用 .clone ,它仍然不会是深层副本。
回答by u7867
One pattern that may work for you is bean-level copying. Basically you use a no-arg constructor and call various setters to provide the data. You can even use the various bean property libraries to set the properties relatively easily. This isn't the same as doing a clone() but for many practical purposes it's fine.
一种可能适合您的模式是 bean 级复制。基本上,您使用无参数构造函数并调用各种设置器来提供数据。您甚至可以使用各种 bean 属性库来相对轻松地设置属性。这与执行 clone() 不同,但对于许多实际目的而言,这很好。
回答by Alepac
I think Yishai answer could be improved so we can have no warning with the following code:
我认为 Yishai 的答案可以改进,因此我们可以使用以下代码没有警告:
public class SomeClass<T extends Copyable<T>> {
public T copy(T object) {
return object.copy();
}
}
interface Copyable<T> {
T copy();
}
This way a class that need to implements Copyable interface has to be like this:
这样一个需要实现 Copyable 接口的类必须是这样的:
public class MyClass implements Copyable<MyClass> {
@Override
public MyClass copy() {
// copy implementation
...
}
}
回答by Bonaparte
Usually, clone() works in tandem with a protected copy constructor. This is done because clone(), unlike a constructor, can be virtual.
通常,clone() 与受保护的复制构造函数协同工作。这样做是因为 clone() 与构造函数不同,它可以是虚拟的。
In a class body for Derived from a super class Base we have
在派生自超类 Base 的类体中,我们有
class Derived extends Base {
}
So, at its most simplistic, you would add to this a virtual copy constructor with the clone(). (In C++, Joshi recommends clone as the virtual copy constructor.)
因此,在最简单的情况下,您可以使用 clone() 向其中添加一个虚拟复制构造函数。(在 C++ 中,Joshi 建议使用 clone 作为虚拟复制构造函数。)
protected Derived() {
super();
}
protected Object clone() throws CloneNotSupportedException {
return new Derived();
}
It gets more complicated if you want to call super.clone() as is recommended and you have to add these members to the class, you can try this
如果你想按照推荐的方式调用 super.clone() 会变得更复杂,你必须将这些成员添加到类中,你可以试试这个
final String name;
Address address;
/// This protected copy constructor - only constructs the object from super-class and
/// sets the final in the object for the derived class.
protected Derived(Base base, String name) {
super(base);
this.name = name;
}
protected Object clone() throws CloneNotSupportedException {
Derived that = new Derived(super.clone(), this.name);
that.address = (Address) this.address.clone();
}
Now, if an execution, you got
现在,如果执行,你有
Base base = (Base) new Derived("name");
and you then did
然后你做了
Base clone = (Base) base.clone();
this would invoke, clone() in the Derived class (the one above), this would invoke super.clone() - which may or may not be implemented, but you're advised to call it. The implementation then passes output of super.clone() to a protected copy constructor that takes a Base and you pass any final members to it.
这将调用派生类(上面的那个)中的 clone(),这将调用 super.clone() - 这可能会或可能不会实现,但建议您调用它。该实现然后将 super.clone() 的输出传递给一个受保护的复制构造函数,该构造函数接受一个 Base 并且您将任何最终成员传递给它。
That copy constructor then invokes the copy constructor of the super-class (if you know that it has one), and sets the finals.
然后该复制构造函数调用超类的复制构造函数(如果您知道它有),并设置决赛。
When you come back to the clone() method, you set any non-final members.
当你回到 clone() 方法时,你设置了任何非最终成员。
Astute readers will notice that if you have a copy-constructor in the Base, it will be called by super.clone() - and will be called again when you invoke the super-constructor in the protected constructor, so you may be calling the super copy-constructor twice. Hopefully, if it is locking resources, it will know that.
细心的读者会注意到,如果你在 Base 中有一个复制构造函数,它会被 super.clone() 调用——并且在你调用受保护构造函数中的超级构造函数时会再次被调用,所以你可能会调用两次超级复制构造函数。希望如果它正在锁定资源,它会知道这一点。
回答by Naresh Joshi
Below are some cons due to which many developers don't use Object.clone()
以下是许多开发人员不使用的一些缺点 Object.clone()
- Using
Object.clone()
method requires us to add lots of syntax to our code like implementCloneable
interface, defineclone()
method and handleCloneNotSupportedException
and finally call toObject.clone()
and cast it our object. Cloneable
interface lacksclone()
method, it is a marker interface and doesn't have any method in it, and still we need to implement it just to tell JVM that we can performclone()
on our object.Object.clone()
is protected so we have to provide our ownclone()
and indirectly callObject.clone()
from it.- We don't have any control over object construction because
Object.clone()
doesn't invoke any constructor. - If we are writing
clone()
method in a child class e.g.Person
then all of its superclasses should defineclone()
method in them or inherit it from another parent class otherwisesuper.clone()
chain will fail. Object.clone()
support only shallow copy, so reference fields of our newly cloned object will still hold objects which fields of our original object was holding. In order to overcome this, we need to implementclone()
in every class who's reference our class is holding and then clone them separately in ourclone()
method like in below example.- We can not manipulate final fields in
Object.clone()
because final fields can only be changed through constructors. In our case, if we want everyPerson
object to be unique by id, we will get the duplicate object if we useObject.clone()
becauseObject.clone()
will not call the constructor and finalid
field can't be modified fromPerson.clone()
.
- 使用
Object.clone()
方法需要我们在我们的代码中添加很多语法,比如实现Cloneable
接口、定义clone()
方法和句柄CloneNotSupportedException
,最后调用Object.clone()
并将其转换为我们的对象。 Cloneable
接口缺少clone()
方法,它是一个标记接口,里面没有任何方法,我们仍然需要实现它只是为了告诉JVM我们可以clone()
对我们的对象执行。Object.clone()
受到保护,因此我们必须提供我们自己的clone()
间接调用Object.clone()
。- 我们对对象构造没有任何控制权,因为
Object.clone()
不调用任何构造函数。 - 如果我们
clone()
在子类中编写方法,例如,Person
那么它的所有超类都应该clone()
在其中定义方法或从另一个父类继承它,否则super.clone()
链将失败。 Object.clone()
仅支持浅拷贝,因此我们新克隆的对象的引用字段仍将持有原始对象的字段所持有的对象。为了克服这个问题,我们需要clone()
在引用我们类所持有的每个类中实现,然后在我们的clone()
方法中分别克隆它们,如下例所示。- 我们不能操作 final 字段,
Object.clone()
因为 final 字段只能通过构造函数改变。在我们的例子中,如果我们希望每个Person
对象的 id 都是唯一的,如果我们使用,我们将得到重复的对象,Object.clone()
因为Object.clone()
不会调用构造函数,并且id
不能从Person.clone()
.
Copy constructors are better than Object.clone()
because they
复制构造函数比Object.clone()
因为它们更好
- Don't force us to implement any interface or throw any exception but we can surely do it if it is required.
- Don't require any casts.
- Don't require us to depend on an unknown object creation mechanism.
- Don't require parent class to follow any contract or implement anything.
- Allow us modify final fields.
- Allow us to have complete control over object creation, we can write our initialization logic in it.
- 不要强迫我们实现任何接口或抛出任何异常,但如果需要,我们肯定可以做到。
- 不需要任何演员表。
- 不要要求我们依赖一个未知的对象创建机制。
- 不要要求父类遵循任何合同或实现任何东西。
- 允许我们修改最终字段。
- 让我们可以完全控制对象的创建,我们可以在其中编写我们的初始化逻辑。
Read more on Java Cloning - Copy Constructor versus Cloning
阅读有关Java 克隆的更多信息- 复制构造函数与克隆
回答by user3996996
The Cloneable interface is broken, in the sense that it is useless but clone works well and can lead to better performance for big objects - 8 fields and more, but it will then fail the escape analysis. so preferable to use copy constructor most of the time. Using clone on array is faster than Arrays.copyOf because the length is guaranteed to be the same.
Cloneable 接口已损坏,从某种意义上说,它没有用,但克隆运行良好,可以为大对象带来更好的性能 - 8 个字段和更多,但它会导致逃逸分析失败。所以大多数时候最好使用复制构造函数。在数组上使用 clone 比 Arrays.copyOf 更快,因为长度保证相同。
more details here https://arnaudroger.github.io/blog/2017/07/17/deep-dive-clone-vs-copy.html
更多细节在这里https://arnaudroger.github.io/blog/2017/07/17/deep-dive-clone-vs-copy.html
回答by sorrymissHymanson
If one is not 100% aware of all the quirks of clone()
, then I would advise to stay away from it. I would not say that clone()
broken. I would say: use it only when you are entirely sure it is your best option.
A copy constructor (or factory method, that doesn't really matter I think) is easy to write (maybe lengthy, but easy), it only copies what you want to be copied, and copies the way you want things to be copied. You can trim it to your exact needs.
如果一个人不是 100% 了解所有的怪癖clone()
,那么我建议远离它。我不会说clone()
坏了。我会说:只有当你完全确定它是你最好的选择时才使用它。复制构造函数(或工厂方法,我认为这并不重要)很容易编写(可能很长,但很容易),它只复制您想要复制的内容,并复制您希望复制内容的方式。您可以根据自己的具体需要修剪它。
Plus: it's easy to debug what happens when you call your copy constructor / factory method.
另外:很容易调试调用复制构造函数/工厂方法时发生的情况。
And clone()
does not create a "deep" copy of your object out of the box, assuming you mean that not only the references (e.g. to a Collection
) are copied over. But read more about deep and shallow here:
Deep copy, shallow copy, clone
并且clone()
不会创建开箱即用的对象的“深层”副本,假设您的意思是不仅引用(例如 a Collection
)被复制。但是在这里阅读更多关于深和浅的信息:
深拷贝、浅拷贝、克隆