Java周二生效! 明智地覆盖`clone`

今天我们在谈论可克隆的界面及其相关克隆功能。 老实说,我过去从未与该方法进行过任何交互,在了解了该方法之后,我不确定我是否真的想这样做,这似乎很容易出错。 因此,让我们深入了解这种方法,以便我们可以就是否使用此方法做出明智的决策。

那是什么可克隆的接口。 它是一个mixin接口告知班级用户可以对该班级执行某些操作。 关于这个特殊的mixin的极其奇怪的事情是,它不需要执行特定的功能,而只是充当一个标志,允许实现类在父类上调用方法。 这意味着实现该类的用户可克隆的不一定总是调用克隆该类上的方法而不求助于反射,即使那样也可能行不通。 话虽这么说,这是宾语类,因此有必要了解它并知道如何实现该方法以及替代方法。 本章有效的Java通过。

那么什么是合同可克隆的? 如上所述,它不包含任何方法,而是充当受保护对象的标志克隆中的方法宾语类。 如果一个班级打电话给克隆上宾语该类实现可克隆的,宾语的实现克隆将返回该对象的逐字段副本。 如果该类未实现可克隆的,a CloneNotSupportedException is thrown. If this use of a interface feels weird that is good,this is not a behavior you should try to mimic in your own classes. The general (although weak) contract is as follows:

  • 实施类应该创建一个公众克隆调用super.克隆()方法。(x.克隆() != x) == true simply, 克隆 should return a new object and not just return the current object.(x.克隆().getClass() == x.getClass() == true这不是绝对要求,但可以预期。x.克隆.equals(x) == true同样,这不是绝对的要求,但确实减少了它如何工作的惊奇。

有人可能以为您可以跳过呼叫super.克隆()自己的方法克隆方法,然后简单地调用构造函数以创建新对象,但这可能会导致扩展类并调用super.克隆()因为它将返回错误类的对象。 如上所述,这实际上并没有违反合同,但是违反了约定。

因此,让我们更深入地研究它的工作原理和实现方法。 实施克隆方法是打电话super.克隆()这将返回调用类的功能齐全的副本。 如果您的类仅包含基元或对不可变对象的引用,那么您可能就需要这样做。 让我们来看一个例子:

@Override
public Address clone() {
  try {
    return (Address) super.clone();
  } catch (CloneNotSupportedException impossible) {
    // This will never happen.
    throw new AssertionException();
  }
}

让我们来看一些有趣的事情。 因为宾语的克隆 method的return type is 宾语我们想将其转换为我们班级的类型。 这很好,因为Java允许协变类型。 简而言之,它允许我们在父类型的位置使用所需类的子类。 此强制转换将始终成功,并允许客户端代码跳过类型强制转换。 接下来的有趣的事情是试着抓。 The 宾语 method的signature includes that it throws a CloneNotSupportedException。 如果类实现了可克隆的接口永远不会引发此异常,这是对已检查异常的不良使用的一个示例,应该是RuntimeException。

因此,让我们看一个示例,其中带有非原始字段的类稍微复杂一些。

public class Stack {
  private Object[] elements;
  private int size;
  private static final int DEFAULT_INITIAL_SIZE = 16;

  public Stack() {
    elements = new Object[DEFAULT_INITIAL_SIZE];
  }

  public void push(Object o) {
    ensureCapacity();
    elements[size++] = o
  }

  public Object pop() {
    if (size == 0) {
      throw new StackEmptyException();
    }

    Object result = elements[--size];
    elements[size] = null;
    return result;
  }

  private ensureCapacity() {
    if (elements.length == size) {
      elements = Arrays.copyOf(elements, 2 * size + 1);
    }
  }
}

所以如果我们想做这个堆类工具可克隆的并试图模仿我们对地址类? 我们最终将得到一个复制品尺寸领域,但宾语在两个实例之间共享的数组。 这将导致许多问题,因此我们需要更进一步。 想想克隆方法作为必须保护原始对象的构造方法的一种。 所以让我们来看一个可行的克隆方法堆类:

@Override
public class Stack clone() {
  try {
    // this gets us a replica with copied size field
    Stack copy = (Stack) super.clone();
    copy.elements = elements.clone();
    return copy;
  } catch (CloneNotSupportedException impossible) {
    throw new AssertionError();
  }
}

现在我们正在有效地克隆我们的堆类。 我们的递归调用克隆 method can solve a lot of problems with the 克隆 method but not all. There are times that you will need to take this further and make deep copies of elements. There are many ways to accomplish this and we won't go over all of them here but it is something to be aware of.

需要考虑的其他事项:

  • 因为克隆方法类似于构造函数,它们不应调用可重写方法。即使宾语的克隆方法抛出CloneNotSupportedException,则您的替代不应该。在设计继承类时,您有两种选择。 实施克隆签名相同的方法宾语给予实施者选择实施的自由可克隆的或不。 另一种选择是实施克隆随便扔CloneNotSupportedException这将阻止克隆。如果您的课程需要线程安全,请记住克隆实施也需要同步。宾语的克隆方法不同步。

那么实现这一点值得吗? 可能不会。 有很多简单的方法可以实现这一目标。 通常,复制构造函数或复制工厂可以以更直接的方式完成工作。 所以在我们地址情况可能看起来像这样:

public class Address(Address originalAddress) { ... }

要么

public static Address newInstance(Address originalAddress) { ... }

那么使用这些方法之一而不是实施有什么好处可克隆的:

  • 他们不依赖于容易出错的字段间复制行为。他们不需要遵循非显而易见且无记录的合同。与最终字段的使用不冲突不需要我们处理不必要的检查异常。它们允许使用作为类实现的接口的类型的参数。 这就是我们在标准库中对集合所做的工作。

长话短说,您可能不应该实施可克隆的接口。 相反,要达到我们拥有的其他模式之一,例如复制构造函数或复制工厂。 通过使用这些方法,您应该会获得更好的体验,并减少错误的代码库。

from: https://dev.to//kylec32/effective-java-tuesday-override-clone-judiciously-4fg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值