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:

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:

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

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!