clone() vs copy constructor vs factory method?

User1 picture User1 · Jul 9, 2009 · Viewed 53.8k times · Source

I did a quick google on implementing clone() in Java and found: 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.

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.

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.

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.

Am I missing something? Any insights would be appreciated.

Answer

Yishai picture Yishai · Jul 9, 2009

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).