【问题标题】:Regarding an Immutable class defensive copying关于不可变类防御性复制
【发布时间】:2012-08-13 14:22:28
【问题描述】:

我有一个关于创建不可变类的问题。以下是我考虑的几点:

  1. 使课程最终化
  2. 使所有成员成为最终成员,在静态块中或在构造函数中显式设置它们
  3. 将所有成员设为私有
  4. 没有修改状态的方法
  5. 要非常小心地限制对可变成员组件的访问(请记住,该字段可能是最终的,但对象仍然可以是可变的。即私有最终日期 imStillMutable)- 有关详细信息,请参阅防御性复制或其近亲复制构造函数。


但是我根本没有完全理解 5 点,请您给我建议或给我看一个例子,其中 5 点在那个例子中是明确的?

【问题讨论】:

    标签: java immutability defensive-copy


    【解决方案1】:

    final 表示指针不能指向另一个引用。 例如:

    final Object obj = new Object();
    obj = new Object(); //Invalid
    

    但是final 不阻止修改对象:

    obj.setWhatever("aaa"); //Perfectly valid
    

    如果您没有限制对成员的访问,那么任何人都可以获取对象并对其进行修改。

    例如:yourClass.getObject().setWhatever("aaa").

    防御性复制意味着getObject() 不会直接返回对象,但会对其进行复制然后返回。这样如果调用者修改了返回的对象,就不会修改类的原始成员。

    【讨论】:

      【解决方案2】:

      第 5 点表明,任何时候您有任何方法会返回与可变对象有关的内容,您都希望创建一个独立于私有状态的副本。例如:

      public final class Foo
      {
          private final List<String> strings;
      
          public Foo(List<String> strings)
          {
              // Defensive copy on construction. Protects from constructing
              // code mutating the list.
              this.strings = new ArrayList<String>(strings);
          }
      
          public List<String> getStrings()
          {
              // Defensive copy on read. Protects from clients mutating the list.
              return new ArrayList<String>(strings);
          }
      }
      

      请注意,防御性复制仅在状态可变时才需要。例如,如果您使用 ImmutableList(例如来自 Guava)作为上述类中的状态,则需要在构造时创建一个新列表(除非输入也是 ImmutableList)但 不是getStrings

      还要注意,在这种情况下String 是不可变的,所以我们不需要复制每个字符串。如果这是List&lt;StringBuilder&gt;,我们需要创建一个新列表和每个元素的新副本,作为防御副本的一部分。如您所见,当您的所有状态也都是不可变的时,生活会变得更简单。

      【讨论】:

      • 我正在搜索这个主题并偶然发现了这个答案。 X 对象不受我们控制且可变且不提供可靠克隆方法的 List 场景下怎么办?
      • @Jitendra:这取决于上下文。 也许您处于可以信任其他代码不会更改值的情况......或者您可能需要自己有效地克隆值。这里没有一个正确的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-19
      • 1970-01-01
      相关资源
      最近更新 更多