3

It is well-known that Cloneable is broken beyond repair (see the discussion in this question for more information).

Last questions on alternatives and "how do I do it right" are few years old:

So I'd like to ask again:

What are the modern day (2014) alternatives to Cloneable?

I am looking for a general-purpose solution. I could imagine the following requirements:

  • Some kind of Copyable interface which the classes will implement: A extends Copyable.
  • Deep copying. If an istance of A references an instance of B, a.copy() should reference a new b.copy().
  • Copy to the specified target: a.copyTo(a1).
  • Polymorphic copying: if B extends A then a.copyTo(b) should copy all the properties of B from a to b.

Of course I can implement all of this by myself, but wouldn't it be reasonable to have standard interfaces for this? Or am I missing something?


A bit of the background for my context. I'm working a lot with JAXB and schema-derived classes. It is often extremely useful to have deep copying for these classes. Few years ago I wrote a couple of JAXB schema compiler plugins to generate copyTo methods which implement the requirements above (and more). I had to use my own runtime API. Now I was revisiting the case and decided to ask if there's a standard solution.

Community
  • 1
  • 1
lexicore
  • 42,748
  • 17
  • 132
  • 221

3 Answers3

4

The mechanism I've used more than once is serialize/deserialize. Of course this only works if all objects and elements are serializable so doesn't work in all cases. If all the objects are serializable then it is a very effective way of deep copying an object. It can be done as follows:

public class SerializationHelper {
public static byte[] serialize(Object object) throws IOException {
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    new ObjectOutputStream(os).writeObject(object);
    return os.toByteArray();
}

@SuppressWarnings("unchecked")
public static <T> T deSerialize(byte[] array) throws IOException, ClassNotFoundException {
    return (T)new ObjectInputStream(new ByteArrayInputStream(array)).readObject();
}

@SuppressWarnings("unchecked")
public static <T> T clone(T object) {
    try {
        return (T)deSerialize(serialize(object));
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
mikea
  • 6,537
  • 19
  • 36
2

First of all, I disagree with people saying that Cloneable is "broken". It is not "broken" -- it does exactly what it is documented to do, which is to cause Object.clone() to throw an exception or not. People don't like it because they assume it is something it isn't -- they assume that it is an interface for objects that can be cloned, which was never what it was supposed to be for.

Java (still) does not have an interface in the standard library for objects that can be cloned. It would be nice if they added such an interface, but they haven't. You could make your own interface, and you can make your own classes implement it, but the problem is that you cannot make existing standard library classes implement it. So that would only be useful if you were only dealing with an ecosystem of your own classes.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • 1
    What about other "almost-de-facto-standard" things? Not `java*` packages but something a lot of people use? Like SLF4J for logging or Joda Time for date/time handling? – lexicore Oct 25 '14 at 10:45
0

The best approach depends on the situation and your needs - what are your performance constraints - if you need deep/shallow copy and how well your classes support serialization.

Copy constructors and factory methods

Are still a valid option. When you need a deep copy and you work with complex objects with dependencies, it can be really complicated to write them. Much faster than serialization/deserialization though.

Serialization/Deserialization

Can be easy to implement and you can copy complex objects with little effort as deep copies. However, you need all the objects to be serializable and transient fields will not be copied. Also, it is way more expensive than copy constructors and other variants.

You do not need to write the serialization/deserialization logic as there are already third party libraries for this, such as:

Apache Commons Serialization Utils - You can just call SerializationUtils.clone()

Reflection

There are also various third party tools using reflection to clone. You can use Apache Commons BeanUtils if a shallow copy is enough for you and you follow JavaBeans convention. Just call BeanUtils.cloneBean().

If you need a deep copy instead, you can use for example:

I have written a blog post comparing the approaches above, where you can find a bit more detail.

Vojtech Ruzicka
  • 16,384
  • 15
  • 63
  • 66