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.
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 theclone()
method.
A class implements the
Cloneable
interface to indicate to theObject.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 exceptionCloneNotSupportedException
being thrown.
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
|