为什么人们害怕使用clone()(在集合和JDK类上)?

有很多次我认为使用clone()并不是一个坏习惯。 是的,我知道这个论点。 布洛赫说这很糟糕。 他确实做到了,但是他说实施clone()是不好的。 另一方面,使用克隆,特别是如果通过可信库(如JDK)正确实现,则可以。

就在昨天,我对我的一个答案进行了讨论,只是build议使用ArrayList clone()是可以的(因为这个原因,没有upvotes)。

如果我们看一下ArrayList@author ,我们可以看到一个熟悉的名字–Josh Bloch。 所以ArrayList (和其他集合clone()上的clone()是完全正确的(只要看看它们的实现)。

Calendar也是一样,也许大部分是java.langjava.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是可变的,那么你不得不担心集合本身是否会被克隆,或者这些元素是否会被克隆…因此实现你自己的函数,在哪里你知道元素还是只是容器将被克隆将使事情变得更清楚。