Valid Java, inclutivejava
Cloneable interface must be implemented when overwriting clone. Cloneable does not define any method.
What does Cloneable mean?
If a class implements Clonable, the Object's clone method will return the Object's domain-by-domain copy, otherwise it will throwCloneNotSupportedException.
Generally, the interface is implemented to indicate the behavior of the class.
The Cloneable interface changes the behavior of the protected method in the superclass.
This is an atypical usage and is not worth doing.
Now that the clone method is overwritten, we need to follow some conventions:
- X. clone ()! = X;
- X. clone (). getClass () = x. getClass ();
- X. clone (). equals (x );
In addition, we must ensure that the clone result does not affect the original object while ensuring the clone method conventions.
For example, in the following case, the clone method is not overwritten and the result of super. clone () is obtained directly:
import java.util.Arrays;public class Stack implements Cloneable { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { this.elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } public boolean isEmpty() { return size == 0; } // Ensure space for at least one more element. private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); }}
As a result, elements in the clone result and elements of the original object reference the same array.
In this case, override the clone method and ensure that the original object is not hurt:
@Overridepublic Stack clone() { try { Stack result = (Stack) super.clone(); result.elements = elements.clone(); return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); }}
Although elements is cloned separately, the premise of this practice is that elements is not final.
In fact, clone is not compatible with the immutable field that references a mutable object.
If the element of the array is of the reference type, a problem still occurs when an element changes.
Take Hashtable as an example. Elements in Hashtable use their internal class Entry.
private static class Entry<K,V> implements Map.Entry<K,V> { int hash; final K key; V value; Entry<K,V> next; protected Entry(int hash, K key, V value, Entry<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } //..}
If you clone elements directly as in the Stack example, the cloned Hashtable changes when an Entry changes.
In Hashtable, clone is overwritten as follows:
/** * Creates a shallow copy of this hashtable. All the structure of the * hashtable itself is copied, but the keys and values are not cloned. * This is a relatively expensive operation. * * @return a clone of the hashtable */public synchronized Object clone() { try { Hashtable<K,V> t = (Hashtable<K,V>) super.clone(); t.table = new Entry[table.length]; for (int i = table.length ; i-- > 0 ; ) { t.table[i] = (table[i] != null) ? (Entry<K,V>) table[i].clone() : null; } t.keySet = null; t.entrySet = null; t.values = null; t.modCount = 0; return t; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(); }}
Given that clone may cause many problems, we have two suggestions:
- Do not extend the Cloneable Interface
- Do not implement the Cloneable interface for classes designed for inheritance