关于不可变类防御性复制

时间:2012-08-17 06:14:18

标签: java immutability defensive-copy

我有一个关于创建Immutable类的查询。以下是我考虑的要点:

  1. 让课程最终
  2. 使所有成员都成为最终成员,明确地设置它们,在静态块中或在构造函数中
  3. 让所有成员保密“
  4. 没有修改状态的方法
  5. 要非常小心限制对可变成员组件的访问(请记住该字段可能是最终的,但该对象仍然可以是可变的。即私有最终日期imStillMutable) - 请参阅防御性复制或其表兄复制构造函数以获取更多信息。

  6. 但是我根本不完全理解这一点,你能告诉或给我一个例子,其中5点在这个例子中是清楚的吗?

2 个答案:

答案 0 :(得分:5)

第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<StringBuilder>,我们需要创建一个新列表和每个元素的新副本作为防御副本的一部分。正如你所看到的,当你所有的状态也是不可变的时候,生活会变得更简单。

答案 1 :(得分:3)

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

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

final并不妨碍修改对象:

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

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

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

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