我无法理解何时需要不可变类的情况。
您是否有遇到过此类要求?或者可以给我们任何一个需要使用此模式的实际示例吗?
我无法理解何时需要不可变类的情况。
您是否有遇到过此类要求?或者可以给我们任何一个需要使用此模式的实际示例吗?
通过不可变性,您可以确保底层不可变对象的行为/状态不会改变,从而获得执行其他操作的额外优势:
您可以轻松地使用多个核心/处理器(并发/并行处理)(因为操作序列不再重要)。
可以针对昂贵的操作进行缓存(因为您确定了相同的结果)。
可以轻松进行调试(因为运行历史记录将不再是一个问题)
不可变对象是指一旦初始化,其状态不会改变的实例。使用这样的对象是根据需求而定的。
不可变类适用于缓存目的,并且它是线程安全的。
使用final关键字并不一定会使某个对象变成不可变的:
public class Scratchpad {
public static void main(String[] args) throws Exception {
SomeData sd = new SomeData("foo");
System.out.println(sd.data); //prints "foo"
voodoo(sd, "data", "bar");
System.out.println(sd.data); //prints "bar"
}
private static void voodoo(Object obj, String fieldName, Object value) throws Exception {
Field f = SomeData.class.getDeclaredField("data");
f.setAccessible(true);
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.set(obj, "bar");
}
}
class SomeData {
final String data;
SomeData(String data) {
this.data = data;
}
}
这只是一个例子,以证明“final”关键字的存在是为了防止程序员错误,而不仅仅是如此。如果没有final关键字重新分配值很容易发生意外,而要去这样做改变值必须是有意的。它存在于文档和防止程序员错误。
const
)。class Person {
public string getName() { ... }
public void registerForNameChange(NameChangedObserver o) { ... }
}
string
不是不可变的,那么 Person
类将无法正确实现 registerForNameChange()
,因为有人可以编写以下代码,有效地修改人的姓名而不触发任何通知。void foo(Person p) {
p.getName().prepend("Mr. ");
}
getName()
返回一个const std::string&
的效果是通过引用返回并防止访问mutators,这意味着在该上下文中不需要不可变类。不可变数据结构在编写递归算法时也很有帮助。例如,假设您正在尝试解决一个3SAT问题。一种方法是执行以下操作:
如果您有一个可变结构来表示问题,那么当您在TRUE分支中简化实例时,您将不得不:
然而,如果你以巧妙的方式编写代码,你可以拥有一个不可变的结构,在这个结构中,任何操作都会返回问题的更新版本(但仍然是不可变的),类似于String.replace
——它不会替换字符串,只是给你一个新的字符串。实现这一点的天真方法是在任何修改时只需复制并创建一个新的“不可变”结构,将其降级为第二种解决方案,即具有所有开销的可变结构,但你可以以更有效的方式实现它。
他们还给了我们一个保证。不可变性的保证意味着我们可以在其基础上扩展,并创建新的模式以提高效率,否则这是不可能的。
来自于《Effective Java》; 不可变类仅仅是指其实例无法被修改的类。每个实例所包含的所有信息都是在创建时提供的,并且在对象的生命周期内都是固定的。Java平台库中包含许多不可变类,包括String、包装的基元类型类和BigInteger以及BigDecimal。这其中有很多好处:不可变类比可变类更容易设计、实现和使用。它们更不容易出错,更加安全。