/ API, JDK, DESIGN, CLONEABLE

Cloneable, a Java design gotcha

Some time ago, I described a couple of surprising design choices in the JDK functional interfaces API.

Lately, during a lesson, a student of mine proposed to shallow-copy an ArrayList by using the clone() method: I thought this is another API gotcha worth writing about.

Cloning an object means a new object is created with the same state as the original one.

As per the JavaDoc:

Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object. The general intent is that, for any object x, the expression:

    x.clone() != x

will be true, and that the expression:

    x.clone().getClass() == x.getClass()

will be true, but these are not absolute requirements. While it is typically the case that:

    x.clone().equals(x)

will be true, this is not an absolute requirement.

— JavaDoc
https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#clone()

If I were to design Cloneable from scratch, this is what I would probably end up doing:

expected design

This way, only classes implementing Cloneable have a clone() method. I believe this is the standard way to use a type system.

With generics, it could even be improved:

generics design

Interestingly enough, the design provided by the JDK is quite different:

real design

Compared to the nominal design:

  • The Cloneable interface has no method defined - it’s a marker interface.
  • The Object class provides the clone() method.

A class implements the Cloneable interface to indicate to the Object.clone() method that it is legal for that method to make a field-for-field copy of instances of that class.

Invoking Object’s clone method on an instance that does not implement the Cloneable interface results in the exception CloneNotSupportedException being thrown.

— JavaDoc
https://docs.oracle.com/javase/10/docs/api/java/lang/Cloneable.html

With that approach, checks are made at runtime instead of compile time. This is not something to expect from a language with static typing!

Conclusion

Put clone() in a box, lock it, throw away the key and forget it ever existed.

The Java API provides two ways to copy a Collection into another one:

  1. The Collection.addAll(Collection<? extends E> c) method
  2. All constructors of standard Java collections provide a constructor that takes a collection as parameter. It cannot be enforced by the type system, but it’s a good practice to provide such a constructor in your custom collections.
Collection<String> coll = Arrays.asList("one", "two", "three");

List<String> list = new ArrayList<>(coll);
Nicolas Fränkel

Nicolas Fränkel

Nicolas Fränkel is a Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with narrower interests like Software Quality, Build Processes and Rich Internet Applications. Currently working for Exoscale. Also double as a teacher in universities and higher education schools, a trainer and triples as a book author.

Read More