这是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. 我们应该防御性复制多深?假设我们复制一个数组,但数组元素是可变的?

有帮助吗?

解决方案

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

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

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

其他提示

  1. 如果你无法改变对象的状态,你不需要防守副本。
  2. 您唯一可以做的是假设T的T的实现,并用 instanceof检查它们
  3. 与2相同。
  4. 以您的自由裁量权。如果您假设数组元素的修改可能会在另一个地方打破您的程序,您也应该复制它们。

在您传递到方法的对象是可变的时,防御性编程事件。一个良好的做法(在有效的Java Book中描述)是使它们不变。

  1. 如果日期类不是最终的,则可以为它写一个包装类,即日期的子类。
  2. 它取决于。可能,不需要克隆它。
  3. 它不应该打扰你。接口的实施者应处理同步问题。一般来说,通过接口而不是它们的实现是一个很好的做法。
  4. 对于标准的java集合,java.util.collections类中有很多实用程序方法,例如纯粹的伯列伯州,旨在用于防御性编程。
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top