返回介绍

12.3 克隆的控制

发布于 2024-10-15 23:56:26 字数 5832 浏览 0 评论 0 收藏 0

为消除克隆能力,大家也许认为只需将 clone() 方法简单地设为 private(私有)即可,但这样是行不通的,因为不能采用一个基础类方法,并使其在衍生类中更“私有”。所以事情并没有这么简单。此外,我们有必要控制一个对象是否能够克隆。对于我们设计的一个类,实际有许多种方案都是可以采取的:

(1) 保持中立,不为克隆做任何事情。也就是说,尽管不可对我们的类克隆,但从它继承的一个类却可根据实际情况决定克隆。只有 Object.clone() 要对类中的字段进行某些合理的操作时,才可以作这方面的决定。

(2) 支持 clone(),采用实现 Cloneable(可克隆)能力的标准操作,并覆盖 clone()。在被覆盖的 clone() 中,可调用 super.clone(),并捕获所有违例(这样可使 clone() 不“掷”出任何违例)。

(3) 有条件地支持克隆。若类容纳了其他对象的句柄,而那些对象也许能够克隆(集合类便是这样的一个例子),就可试着克隆拥有对方句柄的所有对象;如果它们“掷”出了违例,只需让这些违例通过即可。举个例子来说,假设有一个特殊的 Vector,它试图克隆自己容纳的所有对象。编写这样的一个 Vector 时,并不知道客户程序员会把什么形式的对象置入这个 Vector 中,所以并不知道它们是否真的能够克隆。

(4) 不实现 Cloneable(),但是将 clone() 覆盖成 protected,使任何字段都具有正确的复制行为。这样一来,从这个类继承的所有东西都能覆盖 clone(),并调用 super.clone() 来产生正确的复制行为。注意在我们实现方案里,可以而且应该调用 super.clone()——即使那个方法本来预期的是一个 Cloneable 对象(否则会掷出一个违例),因为没有人会在我们这种类型的对象上直接调用它。它只有通过一个衍生类调用;对那个衍生类来说,如果要保证它正常工作,需实现 Cloneable。

(5) 不实现 Cloneable 来试着防止克隆,并覆盖 clone(),以产生一个违例。为使这一设想顺利实现,只有令从它衍生出来的任何类都调用重新定义后的 clone() 里的 suepr.clone()。

(6) 将类设为 final,从而防止克隆。若 clone() 尚未被我们的任何一个上级类覆盖,这一设想便不会成功。若已被覆盖,那么再一次覆盖它,并“掷”出一个 CloneNotSupportedException(克隆不支持)违例。为担保克隆被禁止,将类设为 final 是唯一的办法。除此以外,一旦涉及保密对象或者遇到想对创建的对象数量进行控制的其他情况,应该将所有构建器都设为 private,并提供一个或更多的特殊方法来创建对象。采用这种方式,这些方法就可以限制创建的对象数量以及它们的创建条件——一种特殊情况是第 16 章要介绍的 singleton(独子)方案。

下面这个例子总结了克隆的各种实现方法,然后在层次结构中将其“关闭”:

//: CheckCloneable.java
// Checking to see if a handle can be cloned

// Can't clone this because it doesn't
// override clone():
class Ordinary {}

// Overrides clone, but doesn't implement
// Cloneable:
class WrongClone extends Ordinary {
  public Object clone()
      throws CloneNotSupportedException {
    return super.clone(); // Throws exception
  }
}

// Does all the right things for cloning:
class IsCloneable extends Ordinary 
    implements Cloneable {
  public Object clone() 
      throws CloneNotSupportedException {
    return super.clone();
  }
}

// Turn off cloning by throwing the exception:
class NoMore extends IsCloneable {
  public Object clone() 
      throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
  }
}

class TryMore extends NoMore {
  public Object clone() 
      throws CloneNotSupportedException {
    // Calls NoMore.clone(), throws exception:
    return super.clone();
  }
}

class BackOn extends NoMore {
  private BackOn duplicate(BackOn b) {
    // Somehow make a copy of b
    // and return that copy. This is a dummy
    // copy, just to make the point:
    return new BackOn();
  }
  public Object clone() {
    // Doesn't call NoMore.clone():
    return duplicate(this);
  }
}

// Can't inherit from this, so can't override
// the clone method like in BackOn:
final class ReallyNoMore extends NoMore {}

public class CheckCloneable {
  static Ordinary tryToClone(Ordinary ord) {
    String id = ord.getClass().getName();
    Ordinary x = null;
    if(ord instanceof Cloneable) {
      try {
        System.out.println("Attempting " + id);
        x = (Ordinary)((IsCloneable)ord).clone();
        System.out.println("Cloned " + id);
      } catch(CloneNotSupportedException e) {
        System.out.println(
          "Could not clone " + id);
      }
    }
    return x;
  }
  public static void main(String[] args) {
    // Upcasting:
    Ordinary[] ord = { 
      new IsCloneable(),
      new WrongClone(),
      new NoMore(),
      new TryMore(),
      new BackOn(),
      new ReallyNoMore(),
    };
    Ordinary x = new Ordinary();
    // This won't compile, since clone() is
    // protected in Object:
    //! x = (Ordinary)x.clone();
    // tryToClone() checks first to see if
    // a class implements Cloneable:
    for(int i = 0; i < ord.length; i++)
      tryToClone(ord[i]);
  }
} ///:~

第一个类 Ordinary 代表着大家在本书各处最常见到的类:不支持克隆,但在它正式应用以后,却也不禁止对其克隆。但假如有一个指向 Ordinary 对象的句柄,而且那个对象可能是从一个更深的衍生类上溯造型来的,便不能判断它到底能不能克隆。

WrongClone 类揭示了实现克隆的一种不正确途径。它确实覆盖了 Object.clone(),并将那个方法设为 public,但却没有实现 Cloneable。所以一旦发出对 super.clone() 的调用(由于对 Object.clone() 的一个调用造成的),便会无情地掷出 CloneNotSupportedException 违例。

在 IsCloneable 中,大家看到的才是进行克隆的各种正确行动:先覆盖 clone(),并实现了 Cloneable。但是,这个 clone() 方法以及本例的另外几个方法并不捕获 CloneNotSupportedException 违例,而是任由它通过,并传递给调用者。随后,调用者必须用一个 try-catch 代码块把它包围起来。在我们自己的 clone() 方法中,通常需要在 clone() 内部捕获 CloneNotSupportedException 违例,而不是任由它通过。正如大家以后会理解的那样,对这个例子来说,让它通过是最正确的做法。

类 NoMore 试图按照 Java 设计者打算的那样“关闭”克隆:在衍生类 clone() 中,我们掷出 CloneNotSupportedException 违例。TryMore 类中的 clone() 方法正确地调用 super.clone(),并解析成 NoMore.clone(),后者掷出一个违例并禁止克隆。

但在已被覆盖的 clone() 方法中,假若程序员不遵守调用 super.clone() 的“正确”方法,又会出现什么情况呢?在 BackOn 中,大家可看到实际会发生什么。这个类用一个独立的方法 duplicate() 制作当前对象的一个副本,并在 clone() 内部调用这个方法,而不是调用 super.clone()。违例永远不会产生,而且新类是可以克隆的。因此,我们不能依赖“掷”出一个违例的方法来防止产生一个可克隆的类。唯一安全的方法在 ReallyNoMore 中得到了演示,它设为 final,所以不可继承。这意味着假如 clone() 在 final 类中掷出了一个违例,便不能通过继承来进行修改,并可有效地禁止克隆(不能从一个拥有任意继承级数的类中明确调用 Object.clone();只能调用 super.clone(),它只可访问直接基础类)。因此,只要制作一些涉及安全问题的对象,就最好把那些类设为 final。

在类 CheckCloneable 中,我们看到的第一个类是 tryToClone(),它能接纳任何 Ordinary 对象,并用 instanceof 检查它是否能够克隆。若答案是肯定的,就将对象造型成为一个 IsCloneable,调用 clone(),并将结果造型回 Ordinary,最后捕获有可能产生的任何违例。请注意用运行期类型鉴定(见第 11 章)打印出类名,使自己看到发生的一切情况。

在 main() 中,我们创建了不同类型的 Ordinary 对象,并在数组定义中上溯造型成为 Ordinary。在这之后的头两行代码创建了一个纯粹的 Ordinary 对象,并试图对其克隆。然而,这些代码不会得到编译,因为 clone() 是 Object 中的一个 protected(受到保护的)方法。代码剩余的部分将遍历数组,并试着克隆每个对象,分别报告它们的成功或失败。输出如下:

Attempting IsCloneable
Cloned IsCloneable
Attempting NoMore
Could not clone NoMore
Attempting TryMore
Could not clone TryMore
Attempting BackOn
Cloned BackOn
Attempting ReallyNoMore
Could not clone ReallyNoMore

总之,如果希望一个类能够克隆,那么:

(1) 实现 Cloneable 接口

(2) 覆盖 clone()

(3) 在自己的 clone() 中调用 super.clone()

(4) 在自己的 clone() 中捕获违例

这一系列步骤能达到最理想的效果。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文