不可变对象的所有属性都必须是最终属性吗?
2022-08-31 20:33:07
不可变对象必须具有所有属性吗?final
我会说他们没有。但我不知道我是对的还是错的。
不可变对象必须具有所有属性吗?final
我会说他们没有。但我不知道我是对的还是错的。
不可变对象(所有属性最终)和有效不可变对象(属性不是最终对象,但无法更改)之间的主要区别在于安全发布。
您可以在多线程上下文中安全地发布不可变对象,而不必担心添加同步,这要归功于 Java 内存模型为最终字段提供的保证:
final 字段还允许程序员在没有同步的情况下实现线程安全的不可变对象。线程安全的不可变对象被所有线程视为不可变对象,即使使用数据争用在线程之间传递对不可变对象的引用也是如此。这可以提供安全保证,防止不正确或恶意代码滥用不可变类。必须正确使用 final 字段以提供不可变性的保证。
作为旁注,它还允许强制不可变性(如果您尝试在类的未来版本中更改这些字段,因为您忘记了它应该是不可变的,则不会编译)。
澄清
final List
this
不安全发布的示例:
class EffectivelyImmutable {
static EffectivelyImmutable unsafe;
private int i;
public EffectivelyImmutable (int i) { this.i = i; }
public int get() { return i; }
}
// in some thread
EffectivelyImmutable.unsafe = new EffectivelyImmutable(1);
//in some other thread
if (EffectivelyImmutable.unsafe != null
&& EffectivelyImmutable.unsafe.get() != 1)
System.out.println("What???");
该程序理论上可以打印。如果是最终结果,那将不是法律结果。What???
i
您可以仅通过封装轻松保证不可变性,因此没有必要:
// This is trivially immutable.
public class Foo {
private String bar;
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return bar;
}
}
但是,在某些情况下,您还必须通过封装来保证它,因此这还不够:
public class Womble {
private final List<String> cabbages;
public Womble(List<String> cabbages) {
this.cabbages = cabbages;
}
public List<String> getCabbages() {
return cabbages;
}
}
// ...
Womble w = new Womble(...);
// This might count as mutation in your design. (Or it might not.)
w.getCabbages().add("cabbage");
这样做是为了捕捉一些微不足道的错误,并清楚地证明你的意图,这不是一个坏主意,但是“所有字段都是最终的”和“类是不可变的”不是等效的陈述。