首页 > 学院 > 开发设计 > 正文

Effective Java

2019-11-14 22:26:21
字体:
来源:转载
供稿:网友
Effective java - 谨慎覆盖clone

覆盖clone时需要实现Cloneable接口,Cloneable并没有定义任何方法。那Cloneable的意义是什么?如果一个类实现了Clonable,Object的clone方法就可以返回该对象的逐域拷贝,否则会抛出CloneNotSupportedException

通常,实现接口是为了表明类的行为。而Cloneable接口改变了超类中PRotected方法的行为。这是种非典型用法,不值得仿效。

好了,既然覆盖了clone方法,我们需要遵守一些约定:

  • x.clone() != x;
  • x.clone().getClass() = x.getClass();
  • x.clone().equals(x);

另外,我们必须保证clone结果不能影响原始对象的同时保证clone方法的约定。

比如下面这种情况,没有覆盖clone方法,直接得到super.clone()的结果:

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);    }}

结果可想而知,clone结果的elements和原始对象的elements引用同一个数组。

既然如此,覆盖clone方法,并保证不会伤害到原始对象:

@Overridepublic Stack clone() {    try {        Stack result = (Stack) super.clone();        result.elements = elements.clone();        return result;    } catch (CloneNotSupportedException e) {        throw new AssertionError();    }}

虽然把elements单独拿出来clone了一遍,但这种做法的前提是elements不是final。其实再正常不过,clone无法和引用可变对象的不可变field兼容。

如果数组的元素是引用类型,当某个元素发生改变时仍然会出现问题。此处以Hashtable为例,Hashtable中的元素用其内部类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;    }    //..}

如果像Stack例子中那样直接对elements进行clone,某个Entry发生变化时clone出来的Hashtable也随之发生变化。

于是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();        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();    }}

鉴于clone会导致诸多问题,有两点建议:

  • 不要扩展Cloneable接口
  • 为继承而设计的类不要实现Cloneable接口

上一篇:JDBC的总结

下一篇:Effective Java

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表