/ 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 technologist focusing on cloud-native technologies, DevOps, CI/CD pipelines, and system observability. His focus revolves around creating technical content, delivering talks, and engaging with developer communities to promote the adoption of modern software practices. With a strong background in software, he has worked extensively with the JVM, applying his expertise across various industries. In addition to his technical work, he is the author of several books and regularly shares insights through his blog and open-source contributions.

Read More
Cloneable, a Java design gotcha
Share this