【Effective Java】Ch3_Methods:Item11_谨慎重写clone()

Cloneable接口的目的是作为对象的一个mixin接口,表明对象允许克隆;但这个目的没有达到。

其主要缺点是,Cloneable缺少一个clone()方法,而Object.clone()是受保护的。


通常,实现接口是为了表明类可以为它的客户做些什么;而Cloneable却是改变了超类中受保护方法的行为。


Object.clone()定义的约定:

/**
创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。一般来说,对于任何对象 x,如果表达式: 
x.clone() != x
是正确的,则表达式: 
x.clone().getClass() == x.getClass()将为 true,
但这些不是绝对条件。一般情况下是: 
x.clone().equals(x)将为 true,但这不是绝对条件。 

--按照惯例,返回的对象应该通过调用 super.clone 获得。
如果一个类及其所有的超类(Object 除外)都遵守此约定,则 x.clone().getClass() == x.getClass()。 


--按照惯例,此方法返回的对象应该独立于该对象(正被克隆的对象)。
要获得此独立性,在 super.clone 返回对象之前,有必要对该对象的一个或多个字段进行修改。
这通常意味着要复制包含正在被克隆对象的内部“深层结构”的所有可变对象,并使用对副本的引用替换对这些对象的引用。
如果一个类只包含基本字段或对不变对象的引用,那么通常不需要修改 super.clone 返回的对象中的字段。 


Object 类的 clone 方法执行特定的克隆操作。
首先,如果此对象的类不能实现接口 Cloneable,则会抛出 CloneNotSupportedException。
注意:所有的数组都被视为实现接口 Cloneable。

否则,此方法会创建此对象的类的一个新实例,并像通过分配那样,严格使用此对象相应字段的内容初始化该对象的所有字段;
这些字段的内容没有被自我克隆。所以,此方法执行的是该对象的“浅表复制”,而不“深层复制”操作。 

Object 类本身不实现接口 Cloneable,所以在类为 Object 的对象上调用 clone 方法将会导致在运行时抛出异常。 
*/

protected native Object clone() throws CloneNotSupportedException;

 



所有实现了Cloneable接口的类,都应该提供一个public的clone()方法;

在这个clone()方法中,首先调用super.clone(),然后修正任何需要修正的域。

例如Hashtable.clone()

    /**
     * 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(); // --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;    // --递归调用实例变量.clone()
	    }
	    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();
	}
    }
注意,递归调用实例变量.clone()时,如果该变量为final,则不行! clone架构与饮用可变对象的final域的正常用法是不兼容的!



另一个实现对象拷贝的好办法是提供一个拷贝构造器(copy constructor)或者拷贝工厂(copy factory)。

例如:

public Yum(Yum yum);
public static Yum newInstance(Yum yum);

这种方法比clone有更多优势:

  • 不依赖于某一种有风险的、语言之外的对象创建机制
  • 不要求遵守尚未制定好文档的规范
  • 不会与final域的正常使用发生冲突
  • 不会抛出不必要的受检异常
  • 不需要进行类型转换

另外,拷贝构造器和拷贝工厂可以带参数,参数类型一般是该类实现的接口,以便用户选择拷贝的实现类型。

例如,有一个HashSet s,希望把它拷贝成一个TreeSet,可以调用:new TreeSet(s)

    /**
     * Constructs a new tree set containing the elements in the specified
     * collection, sorted according to the <i>natural ordering</i> of its
     * elements.  All elements inserted into the set must implement the
     * {@link Comparable} interface.  Furthermore, all such elements must be
     * <i>mutually comparable</i>: {@code e1.compareTo(e2)} must not throw a
     * {@code ClassCastException} for any elements {@code e1} and
     * {@code e2} in the set.
     *
     * @param c collection whose elements will comprise the new set
     * @throws ClassCastException if the elements in {@code c} are
     *         not {@link Comparable}, or are not mutually comparable
     * @throws NullPointerException if the specified collection is null
     */
    public TreeSet(Collection<? extends E> c) {
        this();
        addAll(c);
    }










你可能感兴趣的:(java,effective)