我有一个关于创建不可变类的查询。以下是我考虑的几点:
- 使课程最终
- 使所有成员成为最终成员,在静态块中或在构造函数中显式设置它们
- 将所有成员设为私有
- 没有修改状态的方法
- 要非常小心地限制对可变成员组件的访问(记住该字段可能是最终的,但对象仍然可以是可变的。即私有最终日期 imStillMutable) - 请参阅防御性复制或其表亲复制构造函数以获取更多信息。
但是我根本没有完全理解 5 点,您能否建议或给我看一个例子,其中 5 点在那个例子中很清楚?
我有一个关于创建不可变类的查询。以下是我考虑的几点:
但是我根本没有完全理解 5 点,您能否建议或给我看一个例子,其中 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
在上述类中使用 an(例如来自 Guava)作为状态,则需要在构造时创建一个新列表(除非输入也是 an ImmutableList
)而不是in getStrings
。
另请注意,在这种情况下String
是不可变的,因此我们不需要复制每个字符串。如果这是List<StringBuilder>
我们需要创建一个新列表和每个元素的新副本作为防御副本的一部分。如您所见,当您的所有状态也是不可变的时,生活会变得更简单。
final
意味着指针不能指向另一个引用。例如:
final Object obj = new Object();
obj = new Object(); //Invalid
但final
不妨碍修改对象:
obj.setWhatever("aaa"); //Perfectly valid
如果您没有限制对成员的访问,那么任何人都可以获取对象并对其进行修改。
例如:yourClass.getObject().setWhatever("aaa").
防御性复制意味着getObject()
不会直接返回对象,但会对其进行复制然后返回。这样,如果调用者修改了返回的对象,它就不会修改类的原始成员。