java 深克隆 浅克隆
前一段时间,我在JDK功能接口API中描述了几个令人惊讶的设计选择 。
最近,在一堂课上,我的一个学生建议使用clone()方法对ArrayList进行浅拷贝:我认为这是另一个值得一提的API陷阱。
克隆对象意味着将创建一个与原始对象处于相同状态的新对象。
根据JavaDoc:
创建并返回此对象的副本。 “复制”的确切含义可能取决于对象的类别。 通常的意图是,对于任何对象x ,表达式:
x.clone()!=x
将为true ,并且该表达式:
x.clone().getClass()==x.getClass()
将是true ,但这不是绝对要求。 通常情况是:
x.clone().equals(x)
将为true ,这不是绝对要求。
— JavaDoc https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#clone()
如果我要从头开始设计Cloneable ,那么我可能最终会这样做:
这样,只有实现Cloneable类才具有clone()方法。 我相信这是使用类型系统的标准方法。
有了泛型,它甚至可以得到改进:
有趣的是,JDK提供的设计完全不同:
与标称设计相比:
- Cloneable接口没有定义方法-这是一个标记接口。
- Object类提供clone()方法。
一个类实现Cloneable接口,以向Object.clone()方法指示该方法为该类的实例进行逐域复制是合法的。
在未实现Cloneable接口的实例上调用Object的clone方法会导致抛出CloneNotSupportedException异常。
— JavaDoc https://docs.oracle.com/javase/10/docs/api/java/lang/Cloneable.html
使用这种方法,可以在运行时而不是编译时进行检查。 对于静态类型的语言而言,这不是什么期望!
结论
将clone()放在一个盒子中,将其锁定,扔掉密钥,然后忘记它曾经存在过。
Java API提供了两种将Collection复制到另一个的方法:Collection.addAll(Collection <?extends E> c)方法标准Java集合的所有构造函数都提供一个以集合为参数的构造函数。 它不能由类型系统强制执行,但是在自定义集合中提供这样的构造函数是一种好习惯。 Collection <String> coll = Arrays.asList(“一个”,“两个”,“三个”); List <String> list =新的ArrayList <>(coll);