我正在阅读正确执行数组的深层副本,但是我对#clone()
的实现方式感到困惑。它是java.lang.Object
类的成员,但如果您阅读了javadocs:
首先,如果此对象的类未实现Cloneable接口,则抛出CloneNotSupportedException。
那么为什么要首先定义clone
方法呢?当然,如果方法只能在存在接口时使用,则将该方法放在接口中。 Cloneable
接口本身为空;它只是Java使用的标记接口,以确保使用clone
方法是合法的。
这样做也会消除使用泛型来确保类型安全的能力:
class Foo implements Cloneable { // Valid.
@Override
public Object clone() throws CloneNotSupportedException {
// ...
}
}
class TypeSafeFoo implements Cloneable<TypeSafeFoo> { // Not valid.
@Override
public TypeSafeFoo clone() throws CloneNotSupportedException {
// ...
}
}
为什么Java以这种方式完成它?我确信他们有合理的理由,但我似乎无法弄明白。
答案 0 :(得分:20)
Java中的克隆合同规定每个clone
实现必须首先从super.clone()
获取克隆的实例。这将创建一个始终以Object.clone
调用结束的链,并且该方法包含“神奇的”本机级代码,该代码生成表示Java对象的基础原始struct
的二进制副本。如果这个机制不存在,clone
就不会是多态的:Object.clone
方法会生成一个被调用的类的实例;没有本机代码就无法再现。
这就是为什么Object.clone
方法无法避免的原因。 Cloneable
可能包含clone
方法,但会产生有关throws
子句的问题。它的表达方式可以自由声明clone
没有声明的异常,或声明任意异常。如果方法已在界面中声明,则无法实现这种灵活性。
请记住,Generics对于克隆几乎没用:在protected T clone()
中想象Object
:T
来自哪里?我们是否需要Object<T>
并强制 Java Universe 中的每一个类进行参数化,而这一切只是为了使这个半弃用的机制更好地工作?请记住,此代码完全合法:
public class TheMightyOne implements Cloneable {
@Override public TheMightyOne clone() {
return (TheMightyOne) super.clone();
}
}
你可以叫它:
TheMightyOne one = new TheMightyOne();
TheMightyOne two = one.clone(); // do downcasts needed
答案 1 :(得分:4)
要处理创建克隆和基本字段复制,克隆需要继承方法实现。 Cloneable类可以出现在层次结构中的任何位置,并且可能需要扩展特定的超类来执行其主要作业。所有可能的Cloneable类都可以继承实现的唯一超类是Object。
克隆是在仿制药之前定义的。
答案 2 :(得分:0)
clone()
方法是不必要的,每个对象都可以通过反射来调用Object.clone
方法,因此克隆对象取决于是否实现Cloneable
接口。这是出于安全原因。您可以简单地克隆一个通过此util实现Cloneable
的对象:
@SuppressWarnings("unchecked")
public static <T extends Cloneable> T clone(Cloneable obj) {
T rtn = null;
try {
Method method = Object.class.getDeclaredMethod("clone");
method.setAccessible(true);
rtn = (T) method.invoke(obj);
} catch (Throwable t) {
t.printStackTrace();
}
return rtn;
}