当前位置: 首页 > 知识库问答 >
问题:

为什么没有默认的clone()在Java

宋昕
2023-03-14

Java中的Cloneable本质上是不可靠的。具体地说,我对接口的最大问题是,它需要一个不定义方法本身的方法行为。因此,如果遍历一个Cloneable列表,您必须使用反射来访问它定义的行为。然而,在Java8中,我们现在有了默认方法,现在我问为什么在Cloneable中没有默认的clone()方法。

我理解为什么接口不能默认对象方法,但是,这是一个明确的设计决定,所以可以做出例外。

我有点想弃用对象。克隆()并将其内部代码更改为类似以下内容:

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

然后继续使用使clone()作为Clonable中的默认方法的魔法。这并不能真正解决clone()仍然很容易被错误地实现的问题,但这本身就是另一个讨论。

据我所知,这种变化是完全向后兼容的:

  1. 当前覆盖clone()但未实现Cloneable的类(为什么?!)在技术上仍然可以(即使功能上不可能,但这与以前没有什么不同)

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

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

这是否已经在内部讨论过,但从未取得成果?若然,原因为何?如果是因为接口不能默认对象方法的原因,那么在这种情况下做一个例外是否有意义,因为所有继承Cloneable的对象都需要clone()

共有2个答案

夏才
2023-03-14

我的经验可能远不是主流,但我使用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();
        }
    }
}

在这里,即使您实现了Clonable,您也希望使用clone(),但不希望将其公开。相反,您提供了另一个允许克隆另一个名称的方法。因此,在Clonable中使用公共clone()会不必要地污染类的公共接口。

我使用Cloneable的另一个例子是拆分器的实现。trySplit()。请参阅简单拆分器的实现,它返回给定数量的常量对象。它有四个专门化(对象、整数、长整数和双整数),但由于clone()我只能在超类中实现一次trySplit()。同样,我不想公开clone(),我只想自己使用它。

总之,在Cloneable接口中没有clone()方法实际上更灵活,因为它允许我决定是否将其公开。

郎经纶
2023-03-14

你的问题有点宽泛,更像是讨论,但我可以就此事提供一些信息。

在有效Java™中,约书亚·布洛赫详细介绍了这种情况。他以Clonable后面的一点历史开始

Cloneable接口的目的是作为对象的mixin接口来公布它们允许克隆。不幸的是,它未能达到这一目的。它的主要缺陷是缺少克隆方法,并且对象的克隆方法受到保护。如果不诉诸反射,就不能仅仅因为对象实现了Cloneable而对其调用clone方法。

并继续进行推理

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

如果实现可克隆接口要对一个类产生任何影响,那么该类及其所有超类必须遵守一个相当复杂、不可执行且文档稀少的协议。由此产生的机制是语言外的:它创建一个对象而不调用构造函数。

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

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

我认为这足以说明为什么不在接口中使用default方法进行克隆。正确地实施它将是极其复杂的。

 类似资料:
  • 问题内容: Java固有地被破坏了。具体来说,我与接口有关的最大问题是,它期望方法行为无法定义方法本身。因此,如果遍历列表,则必须使用反射来访问其定义的行为。但是,在Java 8中,我们现在有了默认方法,现在我问为什么在中没有默认方法。 我知道为什么接口不能使用默认的Object方法,但是,这是一个明确的设计决定,因此可以进行例外处理。 我有点设想过时,并将其内部代码更改为以下内容: 并且继续进行

  • 我试图理解object.clone()在Java中是如何工作的。我偶然发现了以下事实: null 疑点: 为什么Java设计者会做出这样的设计选择并这样实现它?为什么不在本身中定义,如果实现类不为提供实现,那么将产生编译时错误。 Q2.为什么要在运行时检查实例是否实现? 附:我知道在一个问题中问多个问题是一个坏主意。但这些都是密切相关的问题。我可以问一个问题,比如“为什么不包含?”但是,我觉得,为

  • 为什么没有添加(编辑:或或)(这是一个历史问题),在默认运行时库中是否有一个使用其他方法的简单速记,它做了我没有注意到的相同的事情?

  • 如果我有一个struct Foo和一个struct Bar: 如果我初始化一个条并打印正确得到的值: 但是现在如果我声明这样的构造函数: 我失去了Bar::foo的默认构造,程序输出了32764 0 5! 为什么我不得不像这样无声地初始化每个成员变量: 只要我声明一个构造函数?在这种情况下,为什么默认构造不起作用?

  • 在这个平台上,关于对象类的clone()方法有很多问题。每个人的回答都不一样。关于clone()为什么要受到保护,有很多问题。有人说.. null > 如果上面的第二点是正确的,那么我们如何直接访问子类中的clone()。 什么是不可克隆对象。 有什么需要重写clone()在子类中进行克隆的情况。而另一个包的受保护成员可以通过直接继承其父类在另一个包中访问。 为什么object.clone()没有

  • 在的末尾,我们将 将声明为的默认导出。 然后当我们将这个文件导入到中时,我们简单地将 也以这种方式导入。 但是,中没有包含导出默认寄存器ServiceWorker语句。 我有两个问题- 这是如何工作,而没有一个语句中的? 而且还 为什么registerServiceWorker从一开始就没有大写?如本SO答案所述,它是内置组件吗? 以下是,供参考: