
上QQ阅读APP看书,第一时间看更新
1.2.2 不可变性
我们可将运行中的系统视为正在通过状态空间,而不可变性也与这种视角密切相关。当我们在一个好状态中时,如果能够保持该状态下的一些部分不变,就减少了出错的概率。
下面来看一个阻止除零运算的简单例子。我们将检查除数的值,如果除数为0,就抛出一个错误,如程序清单1.4所示。如果在检查值之后,它还可以发生变化,那么检查的价值就不大了。
程序清单1.4 不当修改

在真实的程序中,这种问题时常以不易察觉的方式出现:变量被另外一个并发执行的线程修改,或者被另外一个调用函数悄悄修改。与本例一样,一旦值发生变化,执行检查的保证就不再有效。如果像程序清单1.5中一样,将x指定为常量,那么在试图修改x时,将发生编译错误。
程序清单1.5 不可变性

编译器将拒绝编译这个bug,并给出下面的错误消息:

从内存表示的角度来说,可变与不可变的x没有区别。常量性只对编译器有意义,它是类型系统启用的一个属性。
通过像这样在类型前面加上const关键字,把状态标记为不可改变,可以阻止修改变量,从而让我们通过检查得到的保证一直有效。当涉及并发时,不可变性特别有用,因为如果数据不可变,就不会发生数据竞争。
当处理不可变变量时,优化编译器可以内联这些变量的值,从而生成更加高效的代码。一些函数编程语言使所有数据不可变:函数接受一些数据作为输入,然后返回其他数据,在此过程中并不会修改输入。在这种情况下,当我们验证一个变量,确认它在好状态下后,就保证了在该变量的整个生存期内,它都会在好状态下。当然,这种处理方式意味着在本可以直接操作数据的地方并不直接操作数据,而是复制数据,这并非我们所期望的。
并不是在所有情况下都可以让所有数据不可变。尽管如此,在合理的情况下让尽可能多的数据不可变,能够显著减少不满足先决条件或者数据竞争等问题。