为什么人们害怕使用clone()(在集合和JDK类上)?
有很多次我认为使用clone()
并不是一个坏习惯。 是的,我知道这个论点。 布洛赫说这很糟糕。 他确实做到了,但是他说实施clone()
是不好的。 另一方面,使用克隆,特别是如果通过可信库(如JDK)正确实现,则可以。
就在昨天,我对我的一个答案进行了讨论,只是build议使用ArrayList
clone()
是可以的(因为这个原因,没有upvotes)。
如果我们看一下ArrayList
的@author
,我们可以看到一个熟悉的名字–Josh Bloch。 所以ArrayList
(和其他集合clone()
上的clone()
是完全正确的(只要看看它们的实现)。
Calendar
也是一样,也许大部分是java.lang
和java.util
类。
那么,给我一个为什么不使用 clone()
和JDK类的理由?
那么,给我一个为什么不使用clone()和JDK类的理由?
- 给定一个
ArrayList
引用,你需要一个getClass
检查来检查它不是JDK类的一个子类。 然后什么? 潜在的子类是不可信的。 据推测,一个小类在某种程度上会有不同的行为。 - 它要求引用比
List
更具体。 有些人不介意,但大多数人认为这是一个坏主意。 - 你必须处理一个演员阵容,在这个演员阵容中不安全。
根据我的经验,clone()的问题出现在派生类上。
比方说, ArrayList
实现了clone(),它返回ArrayList
一个对象。
假设ArrayList
有一个派生类,即MyArrayList
。 如果MyArrayList
不覆盖clone()方法,这将是一个灾难。 (默认情况下它inheritance了ArrayList
的代码)。
MyArrayList
的用户可能期望clone()返回MyArrayList
一个对象; 但是,这是不正确的。
这很烦人:如果一个基类实现了clone(),它的派生类就必须覆盖clone()。
我会用自己的一句话来回答( Josh Bloch在“devise – 复制构造器与克隆”一书中 ):
我使用
Cloneable
东西很less。 我经常提供一个关于具体类的public clone
方法,因为人们期望它。
它不能比这更明确:提供他的集合框架类的clone()
是因为人们期望它 。 如果人们不再期待,他会乐意把它扔掉。 让人们停止期待的方法之一是教育人们停止使用它,而不是主张使用它。
当然,布洛赫本人也表示(不是确切的引用,而是接近)“API就像性:做一个错误,你终身支持它”。 任何public clone()
都可能永远不会被收回。 不过,这不是一个足够好的理由来使用它。
为了不鼓励其他经验较less的开发人员自己实施Clone()
。 我和许多开发人员合作过,他们的代码风格主要是从他们曾经使用过的代码(有时是糟糕的)中复制过来的。
它不强制实施者是否会做深层次或浅层次的复制。
如果你不检查这个克隆方法的实现,使用克隆可能是非常冒险的…因为你可以假设一个克隆impl可以以不同的方式行事…浅层或者深层克隆……有些开发人员可能并不总是检查他们将检索什么样的克隆…
在一个大的应用程序,一个大团队中,也是有风险的,因为使用克隆的时候,如果你修改了克隆实现,你可能会修改应用程序的行为并创build新的bug …并且必须检查每个克隆被调用的地方(但它可以对于像equals,toString等其他对象方法一样…
当修改一个小的子类A的克隆(例如从Deep到Shallow克隆)时,如果B的一个实例有一个对A的引用和一个深度克隆impl,那么由于在A中引用的对象不是浅层克隆,所以B克隆将不再是一个深刻的克隆(对于引用B实例的任何类都是一样的)。 处理克隆方法的深度并不容易。
另外,当你有一个接口(扩展Clonable)和许多(很多!)的实现时,有时你会检查一些impl,看看克隆是深的,并且在接口上调用一个克隆,但是你不能确定在运行时所有的impl真的有深刻的克隆,可以引入错误…
认为可能会更好地impl为每个方法一个shallowClone和一个deepClone方法,并在非常具体的需求实现定制的方法(例如,你想要一个克隆,并限制这个克隆的深度为2,为所有的自定义impl有关课程)。
不要认为在JDK类上使用克隆方法是个大问题,因为它不会被其他开发人员更改,或者至less不会经常更改。 但是如果在编译时不知道类的实现,最好不要在JDK类上调用clone。 我的意思是在一个ArrayList上调用克隆不是一个问题,而是在一个Collection上调用它可能是危险的,因为另一个集合实现可能由另一个开发人员引入(他甚至可以扩展一个Collection impl),没有任何东西告诉你这个impl的克隆会像你期望的那样工作…
如果你的集合中的types是可变的,那么你不得不担心集合本身是否会被克隆,或者这些元素是否会被克隆…因此实现你自己的函数,在哪里你知道元素还是只是容器将被克隆将使事情变得更清楚。