3

这是 Effective java 中防御性复制的一个例子。假设我的基本问题中的场景需要一个防御性副本,并且不能与要求客户避免改变传入的对象的评论有关。

public Period(Date start, Date end) {
    this.start = new Date(start.getTime());
    this.end = new Date(end.getTime());
}

问题:

  1. 如果Date没有构造函数来接收自己,为了使我的自我更通用,传递一个对象而没有复制自身的机制,并且这样的对象不属于我们,即我们不能以任何方式更改它,该怎么办?

  2. 如果构造函数将类型参数作为参数,比如Period(T object)T 可能是可变的,那么需要防御性副本怎么办。我们不知道什么是T。在这种情况下如何进行防御性副本?

  3. 什么是传递接口,其中某些子类确实具有构造函数,例如Date创建自己的对象,而其某些子类没有任何机制可以这样做?

  4. 我们应该在防守端复制多深?假设我们复制了一个数组,但数组元素是可变的?

4

3 回答 3

2
  1. 如果它的所有状态都可用,您可以提取它的状态并自己构造一个新对象。否则,除了使用讨厌的反射或序列化技巧之外,您无能为力。
  2. 如果 T 不是允许复制自身的类的实例,则您无能为力。
  3. 你对此无能为力。
  4. 这取决于。

通过阅读您的问题,您似乎想在任何地方应用“防御性副本”建议。你不应该。大多数时候,使用可变对象的代码需要对原始对象的引用,而不是副本。特别是如果您作为参数得到的是抽象类或接口的实例。

您被迫制作 Date 的防御性副本,因为它是可变的值类型,不应该是可变的,并且如果设计得当,也不会是可变的。如果你提倡值类型的不变性,那么防御性副本就变得不必要了。对于非值类型,您通常不需要副本,而是对对象的引用。

于 2014-01-09T08:11:15.933 回答
0
  1. 如果您无论如何都无法更改对象的状态,则不需要防御性副本。
  2. 您唯一能做的就是假设 T 的可能实现并使用instanceOf检查它们。
  3. 同2。
  4. 由您自行决定。如果您认为修改数组元素可能会在其他地方破坏您的程序,那么您也应该复制它们。
于 2014-01-09T08:25:00.847 回答
0

当传递给方法的对象是可变的时,防御性编程很重要。一个好的做法(在 Effective Java 书中也有描述)是使它们不可变。

  1. 如果 Date 类不是 final 的,你可以为它写一个包装类,即 Date 的子类。
  2. 这取决于。可能不需要克隆它。
  3. 它不应该打扰你。接口的实现者应该处理同步问题。通常,传递接口而不是它们的实现是一种很好的做法。
  4. 对于标准的 java 集合,java.util.Collections 类中有很多实用方法,例如 unmodifiableList 和 unmodifiableMap,旨在用于防御性编程。
于 2014-01-09T08:26:46.450 回答