Effective Java Third edition--13. Overriding the Clone method with caution

Source: Internet
Author: User
Tags unique id

"Effective Java, third Edition" an English version has been published, the second edition of this book presumably many people have read, known as one of the four major Java books, but the second edition of 2009 published, to now nearly 8 years, but with Java 6, 7, 8, and even 9 of the release, the Java language has undergone profound changes.
In the first time here translated into Chinese version. For everyone to learn to share.

13. Rewrite the Clone method with caution

The purpose of the Cloneable interface is to advertise such a class as a Mixin interface (entry 20) that allows cloning. Unfortunately, it did not achieve this goal. Its main disadvantage is the lack of a clone method, and the Clone method of object is protected. You can't, not with reflection (entry 65), invoke the Clone method on the object simply because it implements the Cloneable interface. Even reflection calls may fail because the object cannot be guaranteed to have an accessible clone method. Although there are many flaws, the mechanism is used within a reasonable scope, so it is worthwhile to understand it. This entry tells you how to implement a well-behaved clone method, discuss this method at the appropriate time, and propose alternatives.

Since the Cloneable interface does not contain any methods, what is it used for? It determines the behavior implemented by the protected Clone method of object: If a class implements the Cloneable interface, the Clone method of object returns a copy of the object's attribute-by-property (Field-by-field), otherwise an exception is thrown CloneNotSupportedException . This is a very anomalous interface to use, and should not be emulated. Typically, an interface is implemented to indicate what can be done for a customer. For the Cloneable interface, however, it modifies the behavior of the protected method on the parent class.

Although the specification does not explain, in practice, the class that implements the Cloneable interface wants to provide a normal-running public clone method. To achieve this goal, the class and all its parent classes must follow a complex, non-executable, sparse document protocol. The resulting mechanism is fragile, dangerous, and non-verbal (extralinguistic): It creates objects without calling the constructor method.

The generic specification for the Clone method is weak. The following is copied from the Object specification:

Creates and returns a copy of this object. The exact 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 returns True, and it x.clone().getClass() == x.getClass() also returns true, but they are not absolute requirements, but usually x.clone().equals(x) returns true, and of course this requirement is not absolute.

According to the Convention, the object returned by this method should be obtained by invoking the super.clone method. This is the case if a class and all of its parent classes (except object) adhere to this Convention x.clone().getClass() == x.getClass() .

Depending on the convention, the returned object should be independent of the object being cloned. To achieve this independence, you may need to modify one or more properties of the object returned by Super.clone before returning the object.

This mechanism is similar to the construction method chain (chaining), except that it is not enforced If the clone method of a class returns an instance that is obtained by invoking a constructor method rather than by calling Super.clone, the compiler will not complain, but if the subclass of a class calls Super.clone, the returned object contains the wrong class, preventing the subclass clone Method performs normally. If a class overrides the Clone method with a final modification, then this Convention can be safely ignored because the subclass does not need to worry. However, if a final class has a clone method that does not call Super.clone, then this class has no reason to implement the Cloneable interface because it does not depend on the behavior of object's clone implementation.

Assuming you want to implement the Cloneable interface in a class, its parent class provides a well-behaved clone method. First Call Super.clone. The resulting object will be a replica of the original full function. Any property declared in your class will have the same value as the original property. If each property contains the original value or a reference to an immutable object, the returned object may be exactly what you need, in which case no further processing is required. For example, for classes in entry 11, PhoneNumber This is the case, but be aware that immutable classes should never provide a clone method, because this only wastes replication. With this warning, the following is PhoneNumber the Clone method of the class:

// Clone method for class with no references to mutable state@Override public PhoneNumber clone() {    try {        return (PhoneNumber) super.clone();    } catch (CloneNotSupportedException e) {        throw new AssertionError();  // Can't happen    }}

To make this method work, PhoneNumber the class declaration must be modified to indicate that it implements the Cloneable interface. Although the Clone method of the object class returns the object class, the Clone method returns the PhoneNumber class. This is legal and desirable because Java supports the covariant return type. In other words, the return type of the overridden method can be a subclass of the return type of the overridden method. This eliminates the need for client-side conversions. Before returning, we must cast the result of the super.clone of object to PhoneNumber , but ensure that the cast succeeds.

The invocation of Super.clone is contained in a try-catch block. This is because object declares its clone method to throw CloneNotSupportedException an exception, which is a check-in exception. Because PhoneNumber the Cloneable interface was implemented, we knew that calling Super.clone would succeed. The need to refer here CloneNotSupportedException is that it should be unchecked (entry 71).

If the object contains properties that reference a Mutable object, the simple clone implementation shown earlier may be catastrophic. For example, consider the Stack class in entry 7:

public class Stack {    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;    }    // Ensure space for at least one more element.    private void ensureCapacity() {        if (elements.length == size)            elements = Arrays.copyOf(elements, 2 * size + 1);    }}

Let's say you want this class to be cloned. If the Clone method returns only the object called by Super.clone (), the generated stack instance has the correct value in its Size property, but the elements property references the same array as the original stack instance. Modifying the original instance destroys the invariants in the clone and vice versa. You will soon find that your program produces meaningless results, or throws an NullPointerException exception.

This situation never occurs because the only constructor method in the Stack class is called. In fact, the Clone method is used as another method of construction. You must make sure that it does not damage the original object, and that you can correctly establish invariants on the clone. In order for the Clone method on the stack to work correctly, it must replicate the inside of the Stack object. The simplest approach is to call the Clone method recursively on an array of elements:

// Clone method for class with references to mutable state@Override public Stack clone() {    try {        Stack result = (Stack) super.clone();        result.elements = elements.clone();        return result;    } catch (CloneNotSupportedException e) {        throw new AssertionError();    }}

Note that we do not necessarily convert the results of Elements.clone to object[] arrays. Calling clone on an array returns an array with the same runtime and compile-time type as the array being cloned. This is the preferred idiom for copying arrays. In fact, arrays are the only powerful use of the clone mechanism.

Also note that if the Elements property is final, the previous solution will not work because the clone will be prevented from assigning a new value to the property. This is a basic problem: like serialization, the cloneable architecture is incompatible with the normal use of a final property that references a Mutable object, unless a mutable object can be safely shared between the object and its clones. In order for a class to be cloned, you may want to remove the final modifier from some properties.

Simply calling the Clone method recursively is not always sufficient. For example, suppose you are writing a clone method for a hash table that contains an array of hash buckets, each pointing to the first item in the list of key-value pairs. To improve performance, the class implements its own lightweight single-linked list without using the java.util.LinkedList provided inside Java:

public class HashTable implements Cloneable {    private Entry[] buckets = ...;    private static class Entry {        final Object key;        Object value;        Entry  next;        Entry(Object key, Object value, Entry next) {            this.key   = key;            this.value = value;            this.next  = next;          }    }    ... // Remainder omitted}

Suppose you just recursively clone a hash-bucket array, as we did for stack:

// Broken clone method - results in shared mutable state!@Override public HashTable clone() {    try {        HashTable result = (HashTable) super.clone();        result.buckets = buckets.clone();        return result;    } catch (CloneNotSupportedException e) {        throw new AssertionError();    }}

Although the cloned object has its own array of hash buckets, this array refers to the same linked list as the original array, which can easily lead to undefined behavior in the cloned object and the original object. To solve this problem, you must copy the linked list containing each bucket. The following is a common approach:

Recursive Clone method for class with complex mutable Statepublic class HashTable implements cloneable {private Ent    ry[] Buckets = ...;        private Static class Entry {final Object key;        Object value;        Entry Next;            Entry (Object key, object value, Entry next) {This.key = key;            This.value = value;          This.next = Next; }//recursively copy the linked list headed by this Entry Entry deepcopy () {return new Entry (Ke Y, value, next = null?        Null:next.deepCopy ());            }} @Override Public HashTable clone () {try {HashTable result = (HashTable) super.clone ();            Result.buckets = new Entry[buckets.length]; for (int i = 0; i < buckets.length; i++) if (buckets[i]! = null) result.buckets[i] =            Buckets[i].deepcopy ();        return result; } catch (Clonenotsupportedexception e) {THrow new Assertionerror (); }} ...//remainder omitted}

Private class Hashtable.entry has been expanded to support the deep copy method. The Clone method on Hashtable allocates a new, appropriately sized array of hash buckets, iterates over the original hash bucket array, and deeply replicates each non-empty hash bucket. The Deepcopy method on entry invokes itself recursively to replicate the entire list of nodes beginning with the header. If the hash bucket is not too long, this technique is smart and works fine. However, cloning a linked list is not a good method because it consumes a stack frame for each element in the list. If the list is long, this can easily lead to a stack overflow. To prevent this from happening, you can replace recursion in deepcopy with iterations:

// Iteratively copy the linked list headed by this EntryEntry deepCopy() {   Entry result = new Entry(key, value, next);   for (Entry p = result; p.next != null; p = p.next)      p.next = new Entry(p.next.key, p.next.value, p.next.next);   return result;}

The last way to clone a complex mutable object is to call Super.clone, set all the properties in the resulting object to its initial state, and then call a higher-level method to regenerate the state of the original object. In Hashtable, for example, the bucket property is initialized to a new bucket array, and the put (key, value) method (not shown) is invoked to map the key values in the hash table being cloned. This method usually produces a simple, reasonable and elegant clone method, which runs faster than the method of directly manipulating the internal clones. Although this method is clean, it is antagonistic to the entire cloneable architecture because it blindly overrides the property-by-object replication that makes up the architecture Foundation.

As with the constructor method, the Clone method must not invoke a method that can be overridden during the build process (entry 19). If the Clone method invokes a method that is overridden in a subclass, the subclass has the opportunity to execute the method before it is repaired in the clone, which is likely to result in corruption of the clone and the original object. Therefore, the put (key, value) method that we discussed earlier should be final or private decorated. (If the private adornment, then it is probably a non-final public method of the auxiliary method).

The Clone method of the Object class is declared to throw a Clonenotsupportedexception exception, but is not required when overriding the method. The public clone method should omit the throws clause, since it is easier to use (entry 71) to not throw a check-out exception method.

When designing a class for inheritance (entry 19), there are usually two choices, but no matter which one you choose, you should not implement the Clonable interface. You can choose to mimic the behavior of object by implementing a protected clone method that correctly runs, which is declared to throw a Clonenotsupportedexception exception. This gives the subclass the freedom to implement the Cloneable interface as if it were directly inheriting object. Alternatively, you can choose a clone method that does not implement the work and prevent subclasses from implementing it by providing the following simplified and clone implementation:

// clone method for extendable class not supporting Cloneable@Overrideprotected final Object clone() throws CloneNotSupportedException {    throw new CloneNotSupportedException();}

There is also a notable detail. If you write a thread-safe class that implements Cloneable, remember that its Clone method must be the same as other methods (entry 78) requires proper synchronization. The Clone method of the Object class is not synchronized, so even if its implementation is satisfactory, it may be necessary to write a synchronous clone method that returns Super.clone ().

Recall that all classes that implement cloneable should override the public clone method, and the return type of the method is the class itself. This method should call Super.clone first, and then fix any properties that need to be repaired. Typically, this means copying any mutable objects that contain internal "deep structures," and using references to new objects in place of the references that originally pointed to those objects. Although these internal copies can often be implemented recursively by calling clone, this is not always the best approach. If a class contains only basic types or references to immutable objects, it is likely that there are no properties to fix. There are exceptions to this rule. For example, a property that represents a serial number or other unique ID, even if it is basic or immutable, needs to be fixed.

Is it really necessary to be so complicated? Rarely. If you inherit a class that already implements the Cloneable interface, you have no choice but to implement a well-behaved clone method. Otherwise, it is often preferable to provide another method of object replication. A better approach to object replication is to provide a copy construction method or a replication factory. The copy construction method takes a parameter whose type is the class that contains this constructor method, for example,

// Copy constructorpublic Yum(Yum yum) { ... };

A replication factory is similar to a static factory that replicates construction methods:

// Copy factorypublic static Yum newInstance(Yum yum) { ... };

The copy construction method and its static factory variant have many advantages over Cloneable/clone: they do not rely on a very risky object creation mechanism outside of the language; they do not require adherence to the less-specific conventions; they do not conflict with the proper use of the final attribute; Does not throw unnecessary check exceptions; And no type conversions are required.

In addition, the copy construction method or replication factory can accept parameters for the interfaces that the type implements for the class. For example, by convention, all common collection implementations provide a construction method whose arguments are of type collection or map. Instead of forcing clients to accept the original implementation type, the interface-based replication construction method and Replication factory (more appropriately referred to as the transformation construction method and Transformation factory) allow the client to select the implementation type of the replication. For example, suppose you have a hashset, and you want to copy it as a treeset. The Clone method does not provide this functionality, but using the transform construction method is easy: new TreeSet<>(s) .

Given all the problems associated with the Cloneable interface, the new interface should not inherit it, and the new extensible class should not implement it. Although implementing the Cloneable interface is harmless to the final class, it should be considered a performance-tuning perspective and only in rare cases justified (entry 67). In general, replication functionality is best provided by a construction method or factory. An obvious exception to this rule is the array, which is best copied using the Clone method.

Effective Java Third edition--13. Overriding the Clone method with caution

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.