【发布时间】:2014-03-22 05:55:09
【问题描述】:
问题背景
假设我有一个名为 Transaction 的类和一个名为 NetTransaction 的子类。 Transaction 类实现了一个克隆方法,该方法使用多个构造函数参数构造一个新的 Transaction。
这样的克隆模式给 NetTransaction 等子类带来了问题,因为调用 super.clone 将返回一个 Transaction 类型的对象,该对象不能向上转换为 NetTransaction。因此,我必须在 Transaction 类的 clone 方法中重新实现(复制)代码。显然,这是一种不可接受的模式。
Java 的解决方案——适用于具有内置克隆逻辑或类型反射的语言
在Java中(我已经读过),只要链中的每个覆盖都调用super.clone,调用super.clone总是返回一个正确类型的对象,因为基对象的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 ); }
}
【问题讨论】:
-
我不是投票结束的人,但是您当前发布的代码有什么问题?这是您尝试做的最直接的解决方案。我能想到的唯一改进就是把 A 变成一个叫做 Cloneable 的抽象基类。
-
我提出了一个核心编程问题,一种语言的现有解决方案,以及它为什么存在以及为什么采用某种形式的逻辑推理,我还提出了一个应该适用的替代方案其他类型的语言,但我可能会遗漏一些东西。也许正式学习过计算机科学的人知道是否有其他更好的模式。我已经查看了与此类似的其他问题,但他们没有给出明确的答案,而且他们都专门处理 Java。
-
底线是,对于这个问题,正如所写的那样,即使有人只是回答“是的,那是最好的”,这个问题将比本网站上的任何其他相关问题更有价值我'读过,因为它更清楚地解释了问题,解决了所有的陷阱,替代方案,使用逻辑来描述事物为何如此,提供了通用的解决方案,并最终回答了问题。
-
最佳答案很大程度上取决于您使用的语言以及您要优化的内容 - 可读性/可维护性?静态类型安全?运行速度?运行时灵活性?如果您使用像 C++ 这样的多范式语言,我可以不由自主地想到至少 5 种不同的方法。但是,您列出的示例代码是最直接的,并且最有可能被最多的人理解。哪个“最好”取决于太多因素(包括选择的语言):您的问题不够具体。
-
实际上只有两种方法。即使您直接为对象分配内存并从图像中标记字节,这一切都归结为通过构造函数进行克隆(即预先分配对象类型和内存)或使用一些基本对象实现能够通过反射实例化正确的类型。我很想看到后者在任何情况下都优于前者的案例。由于我们隐含地谈论面向对象的语言,因此解决方案是一种最不常见的分母方法……通过构造函数进行克隆。
标签: clone