在不支持类型反射且没有内置克隆机制的语言中实现"克隆"方法的正确方法是什么?



问题背景

假设我有一个叫做Transaction的类和一个叫做NetTransaction的子类。Transaction类实现了一个克隆方法,该方法使用多个构造函数参数构造新的Transaction。

这样的克隆模式给NetTransaction这样的子类带来了问题,因为调用super.clone将返回一个Transaction类型的对象,该对象无法向上转换到NetTransaction。因此,我必须重新实现(复制)Transaction类的克隆方法中的代码。显然,这是一种不可接受的模式。

Java的解决方案——适用于具有内置克隆逻辑或类型反射的语言

在Java中(我已经读过了),只要链中的每个重写都调用super.clone,调用super.clon总是会返回正确类型的对象,因为基本object的clone方法会自动返回正确类型对象,这可能是运行库中内置的功能。

这种克隆方法的存在意味着每个可克隆对象都必须有一个无参数的默认构造函数(显式或隐式),原因有两个。首先,Object的实现无法为它一无所知的子类选择任意构造函数,因此需要一个无参数构造函数。其次,尽管复制构造函数可能是下一个逻辑选择,但这意味着类链中的每个对象也必须有一个复制构造函数,否则每个复制构造函数都将面临与克隆相同的决定(即调用默认构造函数或复制构造函数)。这最终意味着所有的克隆逻辑都必须在复制构造函数中,这将使覆盖"克隆"变得不必要;因此,我们得出了一个合乎逻辑的结论,即让clone调用除无参数默认构造函数之外的任何东西都会弄巧成拙(即运行时必须创建一个不需要特殊构造逻辑来运行的实例)。

因此,Java的克隆实现似乎也提供了一些内置的浅拷贝,是实现克隆的一种有意义的方式。

没有内置克隆或类型反射的语言的正确替代方案

但是,其他没有这种内置功能并且缺乏类型反射的语言呢?他们应该如何实施克隆?复制构造函数是唯一的方法吗?

我认为唯一真正有意义的方法是复制构造函数,至于为了返回公共接口或基类型或"对象"而实现或重写克隆方法,正确的实现是始终调用当前对象的复制构造函数。这是正确的吗?

模式是,以C#为例:

class A
{
public A( A original_to_copy ) { /*copy fields specific to A*/ }
public object clone() { return new A( this ); }
}
class B: A
{
public B( B original_to_copy ):this (original_to_copy) { /*copy fields specific to B*/ }
public override object clone() { return new B( this ); }
}
class C: B
{
public C( C original_to_copy ):this(original_to_copy) { /*copy fields specific to C*/ }
public override object clone() { return new C( this ); }
}

在没有内置克隆功能的系统中,除了使用复制构造函数的虚拟克隆方法链之外,别无选择。然而,我建议复制构造函数和虚拟克隆方法应该是protected,并且如果传入对象的精确类型与正在构建的对象的精确类型不匹配,则让基类复制构造函数抛出异常。公共克隆方法不应该是虚拟的,而应该链接到虚拟方法并将结果强制转换为它们自己的类型。

在实用的情况下,应该避免让公开公共克隆方法的类是可继承的;消费者应该使用接口类型来引用类实例。如果某个类型的一些使用者需要克隆它,而另一些使用者则不需要,则该类型的一些潜在派生函数在逻辑上无法克隆,并且如果不可克隆的该类型的派生函数应该可由不需要克隆它的代码使用,则以这种方式拆分将允许BaseFoo、CloneableBaseFoo,FancyFoo和CloneableFancy Foo类型的存在;需要奇特能力但不需要克隆对象的代码将能够接受FancyFoo和CloneableFancyFoos对象,而不需要奇特对象但需要克隆能力的代码将可以接受CloneableBaseFoo和cloneableFancy Foo对象。

最新更新