是否需要将Immutable类private
的字段标记为final
,无法更改?
我的意思是仅将字段标记为final
是不够的?
(我知道不可变类没有必要有最终字段,但建议在内部编译时检查该类。)
答案 0 :(得分:7)
这还不够。
考虑这个例子:
final class ImmutableClass {
private final List<String> data;
public ImmutableClass(final List<String> data) {
this.data = data;
}
public List<String> getData() {
return data;
}
}
此类为final
,因此无法扩展。它的data
字段为final
,因此在分配后无法更改。
<强>但是强>:
final ImmutableClass immutableClass = new ImmutableClass(data);
immutableClass.getData().add("Some other value");
糟糕。
因此,要使一个类真正不可变,所有字段也应该是不可变类,或具有防御性副本。
例如,要纠正相关问题,您需要执行此操作:
final class ImmutableClass {
private final List<String> data;
public ImmutableClass(final List<String> data) {
this.data = new ArrayList<>(data);
}
public List<String> getData() {
return Collections.unmodifiableList(data);
}
}
这只有效,因为 String
是不可变的。例如,如果您有List<Date>
,则还需要复制单个Date
个对象。
答案 1 :(得分:1)
final
字段只能从构造函数中分配。因此,在构造之后,使字段final
足以使它们不可变。为可变对象分配指针是另一回事。
答案 2 :(得分:1)
通过getter访问私有变量可以让您更自由地实现类。例如,您可以使用实现来替换一个简单的getter,该实现计算或检索访问时的值或缓存结果。
即使字段是最终字段,将字段保持为私有的另一个原因是字段是可变对象。在这种情况下,对象可以更改,即使它是最终的,通常不是你想要的。这是一个可能是不可变类的示例,其中包含可变Date类的公共final字段。
class DateHolder {
public final Date date;
DateHolder(Date date) {
this.date = date:
}
}
// ...
DateHolder holder = new DateHolder(Date.now());
// this doesn't work because date is final:
//holder.date = new Date(2013, 11, 23);
// but this works even though date is final:
holder.date.setYear(2013);