为什么Java 8中的Cloneable中没有默认的clone()

时间:2015-12-06 20:01:53

标签: java java-8 clone cloneable default-method

Java中的

Cloneable本质上是破碎的。具体来说,我对界面的最大问题是它需要一种不定义方法本身的方法行为。因此,如果遍历Cloneable列表,则必须使用反射来访问其定义的行为。但是,在Java 8中,我们现在有默认方法,现在我问为什么clone()中没有默认的Cloneable方法。

我理解为什么interfaces cannot default Object methods,但这是一个明确的设计决定,因此可以做出例外。

我想要弃用Object.clone()并将其内部代码更改为:

if(this instanceof Cloneable) {
    return ((Cloneable) this).clone();
}
else {
    throw new CloneNotSupportedException();
}

继续使用任何魔法使clone()作为Cloneable中的默认方法。这并没有真正解决clone()仍然可以轻易实现错误的问题,但这本身就是另一种讨论。

据我所知,这种改变完全可以向后兼容:

  1. 当前覆盖clone()但未实现Cloneable(为什么?!)的类在技术上仍然可行(即使在功能上不可行,但这与以前没有什么不同)。< / LI>
  2. 当前覆盖clone()但已实施Cloneable的类在其实现方面的功能仍然相同。
  3. 当前未覆盖clone()但已实现Cloneable(为什么?!)的类现在将遵循规范,即使它完全功能正确。
  4. 那些使用反射并引用Object.clone()的人仍然可以在功能上工作。
  5. super.clone()在功能上仍然相同,即使它引用Object.clone()
  6. 更不用说这将解决Cloneable的一个巨大问题。虽然繁琐且仍然容易错误地实现,但它将解决面向对象的巨大问题。

    我能看到的唯一问题是,实施Cloneable的人没有义务覆盖clone(),但这与以前没有什么不同。

    这是否在内部进行了讨论,但从未实现过?如果是这样,为什么?如果是因为接口不能默认使用Object方法,那么在这种情况下做出异常是否有意义,因为继承Cloneable的所有对象无论如何都期待clone()

2 个答案:

答案 0 :(得分:8)

你的问题有点广泛而且更多的讨论,但我可以对这个问题有所了解。

Effective Java™中,Joshua Bloch给出了相关情况的简要说明。他打开了Cloneable

背后的一些历史
  

Cloneable接口旨在作为对象的mixin接口   宣传他们允许克隆。不幸的是,它没有达到这个目的。它的主要缺陷是缺少克隆方法,而Object的克隆方法受到保护。如果不依靠反射,就不能仅仅因为它实现Cloneable而在对象上调用clone方法。

继续推理

  

[Cloneable]确定Object的受保护克隆实现的行为:如果一个类实现Cloneable,则Object的clone方法返回该对象的逐个字段副本...这是一个非常非典型的接口使用,而不是一个被模仿。通常,实现接口会说明类可以为其客户做些什么。在Cloneable的情况下,它修改了超类上受保护方法的行为。

  

如果实现Cloneable接口对类有任何影响,那么   class及其所有超类必须服从相当复杂,不可执行的,并且   细致记录的协议。结果机制是extralinguistic:它创建一个对象而不调用构造函数。

这里有很多细节,但要注意一个问题:

  

克隆体系结构与引用可变对象的最终字段的正常使用不兼容。

我认为这足以说明在接口中使用default方法进行克隆。正确实施它会非常复杂。

答案 1 :(得分:6)

我的经验可能远不是主流,但我使用clone()并支持Cloneable的当前设计。可能最好将其作为注释,但Cloneable早在注释之前出现。我的观点是Cloneable是一个低级别的东西,没有人应该做obj instanceof Cloneable之类的事情。如果在某些业务逻辑中使用Cloneable,那么声明自己的接口或抽象类会更好地公开clone()并在所有业务逻辑对象中实现它。有时您可能不希望实际公开clone(),而是创建自己的内部使用clone()的方法。

例如,假设您有一个命名对象的层次结构,其中名称在构造后无法更改,但您希望允许使用新名称克隆它们。您可以创建一些这样的抽象类:

public abstract class NamedObject implements Cloneable {
    private String name;

    protected NamedObject(String name) {
        this.name = name;
    }

    public final String getName() {
        return name;
    }

    public NamedObject clone(String newName) {
        try {
            NamedObject clone = (NamedObject)super.clone();
            clone.name = newName;
            return clone;
        }
        catch(CloneNotSupportedException ex) {
            throw new AssertionError();
        }
    }
}

即使您实施Cloneable,也要使用clone(),但不想公开公开它。相反,您提供了另一种允许使用其他名称进行克隆的方法。因此,在clone()中使用公共Cloneable会不必要地污染您的类的公共接口。

我使用Cloneable的另一个案例是Spliterator.trySplit()的实现。请参阅简单spliterator的implementation,它返回给定数量的常量对象。它有四个特化(对象,整数,长整数和双精度),但是由于clone(),我只能在超类中实现trySplit()一次。同样,我不想公开clone(),我只想自己使用它。

总而言之,在clone()界面中没有Cloneable方法实际上更灵活,因为它允许我决定是否要公开它。