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

Why no default clone() in Cloneable in Java 8

Cloneable in Java 本身就坏了。具体来说,我对接口最大的问题是它需要一个不定义方法本身的方法行为。因此,如果遍历 Cloneable 列表,您必须使用反射来访问其定义的行为。但是,在 Java 8 中,我们现在有了默认方法,现在我问为什么 Cloneable.

中没有默认的 clone() 方法

我理解为什么 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(为什么?!)在技术上仍然可以(即使在功能上不可能,但这也没有什么不同)以前是)。
  2. 类 当前覆盖 clone(),但确实实现了 Cloneable,但其实现仍将发挥相同的作用。
  3. 类 当前未覆盖 clone(),但确实实现了 Cloneable(为什么?!)现在将遵循规范,即使它不是 完全 功能正确。
  4. 那些使用反射并引用 Object.clone() 的功能仍然有效。
  5. super.clone() 即使它引用 Object.clone().
  6. 在功能上仍然是相同的

更不用说这将解决 Cloneable 的一个大问题。虽然乏味并且仍然容易错误地实现,但它将解决一个巨大的面向对象的接口问题。

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

这是否在内部讨论过,但从未实现?如果是这样,为什么?如果是因为接口不能默认 Object 方法的原因,那么在这种情况下做一个例外是否有意义,因为所有继承 Cloneable 的对象无论如何都期望 clone()

你的问题有点宽泛,更多的是讨论,但我可以阐明这个问题。

Effective Java™ 中,Joshua Bloch 给出了相当详细的情况概述。他以 Cloneable

背后的一些历史作为开场白

The Cloneable interface was intended as a mixin interface for objects to advertise that they permit cloning. Unfortunately, it fails to serve this purpose. Its primary flaw is that it lacks a clone method, and Object’s clone method is protected. You cannot, without resorting to reflection, invoke the clone method on an object merely because it implements Cloneable.

继续推理

[Cloneable] determines the behavior of Object’s protected clone implementation: if a class implements Cloneable, Object’s clone method returns a field-by-field copy of the object... This is a highly atypical use of interfaces and not one to be emulated. Normally, implementing an interface says something about what a class can do for its clients. In the case of Cloneable, it modifies the behavior of a protected method on a superclass.

If implementing the Cloneable interface is to have any effect on a class, the class and all of its superclasses must obey a fairly complex, unenforceable, and thinly documented protocol. The resulting mechanism is extralinguistic: it creates an object without calling a constructor.

其中涉及很多细节,但只需要注意一个问题:

The clone architecture is incompatible with normal use of final fields referring to mutable objects.

我认为这足以反对在界面中使用 default 方法进行克隆。正确实现它会非常复杂。

我的体验可能离主流还差很远,但是我用的是clone(),支持目前Cloneable的设计。将它作为注释可能会更好,但是 Cloneable 出现在注释之前很久。我的意见是 Cloneable 是一个低级的东西,没有人应该做像 obj instanceof Cloneable 这样的事情。如果您在某些业务逻辑中使用 Cloneable,最好声明您自己的接口或抽象 class 将 clone() 公开给 public 并在您的所有应用程序中实现它业务逻辑对象。有时您可能不想实际公开 clone(),而是创建自己的内部使用 clone() 的方法。

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

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(),但不想 public 公开它。相反,您提供了另一种允许使用其他名称进行克隆的方法。因此在 Cloneable 中使用 public clone() 会不必要地污染 classes 的 public 接口。

我使用Cloneable的另一种情况是Spliterator.trySplit()的实现。请参阅简单拆分器的 implementation,其中 returns 给定数量的常量对象。它有四种特化(针对对象、整数、长整数和双精度),但多亏了 clone(),我可以在超级 class 中只实现一次 trySplit()。再说一次,我不想暴露clone(),我只想自己用

总而言之,在 Cloneable 接口中没有 clone() 方法实际上更灵活,因为它允许我决定是否要使用它 public。