Java 如何处理防御性编程的各种情况?

Java 如何处理防御性编程的各种情况?,java,generics,constructor,defensive-copy,Java,Generics,Constructor,Defensive Copy,这是一个有效java中的防御复制示例。假设我的基本问题中的场景需要一个防御性副本,而不能使用注释要求客户端避免对传入的对象进行变异 public Period(Date start, Date end) { this.start = new Date(start.getTime()); this.end = new Date(end.getTime()); } 问题: 如果Date没有一个构造函数来容纳它自己,那么该怎么办呢?为了使我自己更一般,一个对象在传递时没有复制它自己的

这是一个有效java中的防御复制示例。假设我的基本问题中的场景需要一个防御性副本,而不能使用注释要求客户端避免对传入的对象进行变异

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

  • 如果
    Date
    没有一个构造函数来容纳它自己,那么该怎么办呢?为了使我自己更一般,一个对象在传递时没有复制它自己的机制,而这样的对象不属于我们,即我们不能以任何方式更改它

  • 如果构造函数将类型参数作为参数,比如说
    Period(T object)
    ,并且T可能是可变的,因此需要一个防御性的副本,该怎么办。我们不知道什么是T。在这种情况下如何进行防御性复制

  • 当它的一些子类确实有一个构造函数(如
    Date
    )来创建它自己的对象,而它的一些子类没有任何机制来创建对象时,传递什么是接口

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

  • 如果其所有状态都可用,则可以提取其状态并自行构造新对象。否则,除了使用令人讨厌的反射或序列化技巧之外,您不能对它做任何事情
  • 如果T不是一个允许复制自身的类的实例,那么您不能做任何事情
  • 你对此无能为力
  • 视情况而定
  • 通过阅读您的问题,您似乎希望在任何地方都应用“防御性抄袭”建议。你不应该。大多数情况下,使用可变对象的代码希望引用原始对象,而不是副本。特别是当您得到的参数是抽象类或接口的实例时

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

  • 如果您无法更改对象的状态,则不需要防御性副本
  • 您唯一能做的就是假设T的可能实现,并用instanceOf检查它们
  • 与2相同
  • 由你决定。如果您认为修改数组元素可能会在其他地方破坏您的程序,那么您也应该复制它们

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

  • 如果Date类不是final,您可以为它编写一个包装类,即Date的子类
  • 视情况而定。也许,没有必要克隆它
  • 这不应该打扰你。接口的实现者应该负责同步问题。通常,传递接口而不是它们的实现是一种好的做法
  • 对于标准java集合,java.util.collections类中有许多实用程序方法,如unmodifiableList和unmodifiableMap,用于防御编程

  • 我会说,在某些情况下,这种情况会变成一种代码气味。对于一个复杂的对象,这意味着你在某个地方保存状态,我会考虑取消状态保存。在所有情况下,这不是正确的做法,但在任何情况下都应该提出这个问题。至于当你真的需要这样做的时候,我会说你不得不手工编写定制的复制代码,或者定义一些新的小的、不可变的对象,只复制它实际需要的数据。记住,这些不是严格的通用规则。这要视情况而定。你不应该刻意复制所有的参数(我会在这个基础上拒绝你的代码),当你做不好的时候,要么改变代码(如果它真的需要是一个不可变的对象),要么就让它去做。你还应该检查开始和结束日期是否为空。如果你不这样做,你可能会得到一个NullPointerException。@Gynnad:如果日期是强制性的,抛出NullPointerException是正确的做法。这个建议在理论上听起来不错,但在现实生活中,实现我自己的
    Date
    包装器相当麻烦。我不确定最后两个对这个问题有什么意义。
    unmodifiableX
    方法必须在类外使用;它们作为防御复制机制是无用的,因为底层对象可以在类之外修改。它们旨在防止其他对象修改代码生成的数据,这在某种程度上与防御性复制相反。同步与防御性复制是不同的问题。(只是解释我的反对票。)