如何处理各种防御性编程案例?

时间:2014-01-09 08:01:09

标签: java generics constructor defensive-copy

这是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. 我们应该在多大程度上防御性地复制?让我们说我们复制一个数组,但数组元素是可变的?

3 个答案:

答案 0 :(得分:2)

  1. 如果所有状态都可用,您可以自己提取状态并构造新对象。否则,除了使用令人​​讨厌的反射或序列化技巧之外,你无法做任何事情。
  2. 如果T不是允许自我复制的类的实例,则无法执行任何操作。
  3. 你无能为力。
  4. 取决于。
  5. 通过阅读您的问题,您似乎希望在任何地方应用“防御性副本”建议。你不应该。大多数情况下,使用可变对象的代码需要引用原始对象,而不是副本。特别是如果你得到的参数是抽象类或接口的实例。

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

答案 1 :(得分:0)

  1. 如果无论如何都无法改变对象的状态,则不需要防御性副本。
  2. 您唯一能做的就是假设可能实施T并使用 instanceOf 进行检查。
  3. 与2相同。
  4. 由您自行决定。如果你认为修改数组元素可能会破坏你在其他地方的程序,你也应该复制它们。

答案 2 :(得分:0)

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

  1. 如果Date类不是final,你可以为它编写一个包装类,它是Date的子类。
  2. 这取决于。可能没有必要克隆它。
  3. 不应该打扰你。接口的实现者应该处理同步问题。通常,传递接口而不是其实现是一种好习惯。
  4. 对于标准java集合,java.util.Collections类中有许多实用程序方法,例如unmodifiableList和unmodifiableMap,用于防御性编程。