Java: Rationale of the Cloneable Interface

Java: Rationale of the Cloneable interface

Basically, it's a broken interface. Ken Arnold and Bill Venners discussed it in Java Design Issues.

Arnold:

If I were to be God at this point, and many people are probably glad I am not, I would say deprecate Cloneable and have a Copyable, because Cloneable has problems. Besides the fact that it's misspelled, Cloneable doesn't contain the clone method. That means you can't test if something is an instance of Cloneable, cast it to Cloneable, and invoke clone. You have to use reflection again, which is awful. That is only one problem, but one I'd certainly solve.

Why we use cloneable interface in java while making clones of the object?

The clone situation is unique and is rather anti-java-like - there's a reason its not recommended to use it in the first place. The way Cloneable is used in the JDK is not something you should be taking notes on - this isn't how to design APIs, nothing else in the java ecosystem works this way.

Weirdly, this is a common thing: The core parts of java often aren't java-like. For example, nobody in their right mind would make an API that defines an application by 'it has a method with the signature public static void main(String[] args). The obvious design principle would be to make an abstract class or interface with an abstract start() method, and to make a java app you write a class that has a no-args constructor and implements/extends that interface/class. Same story for arrays: They are weird - their toString, equals, and hashCode implementations are surprising, to say the least. They aren't entirely type-safe either.

The reasons for all of this are historic: To explain them, you need to know about all sorts of things that were relevant at the time, but aren't important anymore and haven't been for decades.

Cloneable is no different. Let me explain why it works that way:

The 'system' of cloning is offered by the JDK itself, it's a built-in thing that an ordinary library could not easily make. Kinda how "java SomeClass invokes its main method" is part of java itself.

The system, however, needs an opt-in mechanism: The act of cloning may not make sense (what does it mean to 'clone' an InputStream representing a TCP network connection's incoming bytes? What does it mean to 'clone' the value of an enum which tries to guarantee that only one instance ever exists? What does it mean to 'clone' a singleton?)

Thus, 'just make all objects cloneable' is dangerous, so java didn't want to do that: They want you to opt in to it. Java wants the author of a class to explicitly say: Yup. I am clonable, using the standard mechanism (which deep-copies all fields, if memory serves).

That is what Cloneable is for! - that's how you say: Yup. I'm good with it. By implementing that. It's a flag.

Java could also have decided to do something like:

/** @cloneable */
public class Something {}

instead, but they didn't. If it had been designed in this more modern age, perhaps it would have looked like:

@Cloneable
public class Something {}

But annotations were introduced in java 1.5 as a demo feature in 1.6 properly. The cloneable interface is as old as java 1.0 - over a decade earlier. "add an interface that defines nothing" was the standard way to flag class properties back then, even if it isn't now.

NB: You don't just implements Cloneable, you also make a public clone method. The implements Cloneable part tells the cloning system: You may clone this class, for example even if it is part of the deep structure of an encompassing object that is being cloned. Making a clone method that invokes the protected JVM-provided clone() method that j.l.Object has is how you expose the API. Maybe you want to name it copy instead, or maybe you want cloning but not as part of your public API. Your question isn't about how to use clone, but why it works like it is - my advice if you want to use it is simple. Don't, write clone code yourself, or better yet, design your API with more immutables so that cloning is no longer neccessary.

Why does java.lang.Cloneable not override the clone() method in java.lang.Object?

Because it's a poorly-designed interface.

From Effective Java (sorry, Google Books does not have a preview for the 2nd edition):

Item 11: Override clone judiciously


The Cloneable interface was intended as a mixin interface (Item
18) for objects to advertise that they permit cloning. Unfortunately,
it fails to serve this purpose. Its primary flaw is that it lacks a
clone method, and Object's clone method is protected. You
cannot, with resorting to reflection (Item 53), invoke the clone
method on an object merely because it implements Cloneable. Even a
reflective invocation may fail, as there is no guarantee that the
object has an accessible clone method.

Purpose of cloning in Java

IMO you question has two parts.

What is the need for the Cloneable interface?

The Cloneable interface is also known as a marker interface, which means that it does not have any methods, its whole purpose is to tell you, the user of that class, that it implements the clone() method which is inherited from Object. This enables you to do a check before calling the clone() method:

Animal a = new Dog();
Animal b;
if (a instanceof Cloneable)
b = a.clone();

This happens pretty often in Java; see for example the Serializable interface.

Why does Java need a clone() method at all, as it already has copy constructors?

The very short answer is polymorphism. How would you correctly clone a Dog instance, through a reference to an Animal, its super class, if you didn't have the clone() method?

What is the point in letting my class implement Cloneable?

To implement the clone method, you simply do:

public Object clone() throws CloneNotSupportedException {
return super.clone();
}

You can of course customize the method to make a deeper copy if needed.

Calling super.clone() is almost mandatory because, unless the class is final and thus can't be overridden, the clone() method must return an instance of the same class as the object on which it's called. So simply creating a new instance and copy the state will work for this class, but not for all the subclasses. Moreover, you don't always have access to all the state contained in superclasses.

In short, you make the protected clone method of Object public. And the first thing that the Object.clone() method does is (this is not the real code, but this is what the method does):

if (!(this instanceof Cloneable)) {
throw new CloneNotSupportedException();
}

So, Cloneable is just a marker interface to let the Object.clone() method know that it must not throw an exception when called.

This is one of the most badly-designed parts of Java. Usually, you should prefer using a copy contructor instead of using clone().

Why is Cloneable not deprecated?

There is a bug submitted in 1997 to Java Bug Database about adding clone() method to Cloneable, so it would no longer be useless. It was closed with resolution "won't fix" and justification was as follows:

Sun's Technical Review Committee (TRC) considered this issue at length
and recommended against taking any action other than improving the
documentation of the current Cloneable interface
. Here is the full
text of the recommendation:

The existing Java object cloning APIs are problematic. There is a
protected "clone" method on java.lang.Object and there is an interface
java.lang.Cloneable. The intention is that if a class wants to allow
other people to clone it, then it should support the Cloneable
interface and override the default protected clone method with a
public clone method. Unfortunately, for reasons conveniently lost in
the mists of time, the Cloneable interface does not define a clone
method.

This combination results in a fair amount of confusion. Some classes
claim to support Cloneable, but accidentally forget to support the
clone method. Developers are confused about how Cloneable is supposed
to work and what clone is supposed to do.

Unfortunately, adding a "clone" method to Cloneable would be an
incompatible change. It won't break binary compatibility, but it will
break source compatibility. Anecdotal evidence suggests that in
practice there are a number of cases where classes support the
Cloneable interface but fail to provide a public clone method. After
discussion, TRC unanimously recommended that we should NOT modify the
existing Cloneable interface, because of the compatibility impact.

An alternative proposal was to add a new interface
java.lang.PubliclyCloneable to reflect the original intended purpose
of Cloneable. By a 5 to 2 majority, TRC recommended against this.
The main concern was that this would add yet more confusion (including
spelling confusion!) to an already confused picture.

TRC unanimously recommended that we should add additional
documentation to the existing Cloneable interface
to better describe
how it is intended to be used and to describe "best practices" for
implementors.

So, although this is not directly about deprecated, the reason for not making Cloneable "deprecated" is that Technical Review Comitee decided that modifying existing documentation will be sufficient enough to make this interface useful. And so they did. Until Java 1.4, Cloneable was documented as follows:

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.

Attempts to clone instances that do not implement the Cloneable
interface result in the exception CloneNotSupportedException being
thrown.

The interface Cloneable declares no methods.

Since Java 1.4 (which was released in February 2002) up to current edition (Java 8) it looks like this:

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.

By convention, classes that implement this interface should override
Object.clone (which is protected) with a public method. See
Object.clone() for details on overriding this method.

Note that this interface does not contain the clone method. Therefore,
it is not possible to clone an object merely by virtue of the fact
that it implements this interface. Even if the clone method is invoked
reflectively, there is no guarantee that it will succeed.

What is the point of the cloneable interface?

To answer your first question, you must implement the Clonable interface for the clone() method to work since Object's clone checks that the interface is implemented otherwise it will throw an exception.

Using clone() is a bad idea. It was one of the original ideas in Java that didn't work out. There are some design issues including it usually being a shallow copy. If you want to make a copy or a deep copy of an object, it's best to create a copy constructor or a copy method to do it without using clone. Using clone() correctly is hard.

If really you want to use the clone(), then read this blog post which explains how to use clone(). Basically there are 5 points to remember when using clone():

1) Clone method is used to create a copy of object in Java. In order to use clone() method, class must implement java.lang.Cloneable interface and override protected clone() method from java.lang.Object. A call to clone() method will result in CloneNotSupportedException, if that class doesn't implement Cloneable interface.

2) No constructor is called during cloning of Object in Java.

3) Default implementation of clone() method in Java provides "shallow copy" of object, because it creates copy of Object by creating new instance and then copying content by assignment, which means if your Class contains a mutable field, then both original object and clone will refer to same internal object. This can be dangerous, because any change made on that mutable field will reflect in both original and copy object. In order to avoid this, override clone() method to provide deep copy of object.

4) By convention, clone of an instance should be obtained by calling super.clone() method, this will help to preserve invariant of object created by clone() method i.e. clone != original and clone.getClass() == original.getClass(). Though these are not absolute requirement as mentioned in Javadoc.

5) Shallow copy of an instance is fine, until it only contains primitives and Immutable objects, otherwise, you need to modify one or more mutable fields of object returned by super.clone, before returning it to caller.



Related Topics



Leave a reply



Submit