Effective Java - 小心覆盖clone
发布时间:2021-12-11 09:56:43 所属栏目:PHP教程 来源:互联网
导读:覆盖clone时需要实现Cloneable接口,Cloneable并没有定义任何方法。 那Cloneable的意义是什么? 如果一个类实现了Clonable,Object的clone方法就可以返回该对象的逐域拷贝,否则会抛出CloneNotSupportedException。 通常,实现接口是为了表明类的行为。 而Cl
覆盖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方法,并保证不会伤害到原始对象: @Override public 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接口 (编辑:应用网_丽江站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |