《Kotlin核心编程》 ——2.2.2 优先使用val来避免副作用
2.2.2 优先使用val来避免副作用
在很多Kotlin的学习资料中,都会传递一个原则:优先使用val来声明变量。这相当正确,但更好的理解可以是:尽可能采用val、不可变对象及纯函数来设计程序。关于纯函数的概念,其实就是没有副作用的函数,具备引用透明性,我们会在第10章专门探讨这些概念。由于后续的内容我们会经常使用副作用来描述程序的设计,所以我们先大概了解一下什么是副作用。
简单来说,副作用就是修改了某处的某些东西,比方说:
修改了外部变量的值。
IO操作,如写数据到磁盘。
UI操作,如修改了一个按钮的可操作状态。
来看个实际的例子:我们先用var来声明一个变量a,然后在count函数内部对其进行自增操作。
var a = 1
fun count(x: Int) {
a = a + 1
println(x + a)
}
>>> count(1)
3
>>> count(1)
4
在以上代码中,我们会发现多次调用count(1)得到的结果并不相同,显然这是受到了外部变量 a 的影响,这个就是典型的副作用。如果我们把var换成val,然后再执行类似的操作,编译就会报错。
val a = 1
>>> a = a + 1
error: val cannot be ressigned
这就有效避免了之前的情况。当然,这并不意味着用val声明变量后就不能再对该变量进行赋值,事实上,Kotlin也支持我们在一开始不定义val变量的取值,随后再进行赋值。然而,因为引用不可变,val声明的变量只能被赋值一次,且在声明时不能省略变量类型,如下所示:
fun main(args: Array<String>) {
val a: Int
a = 1
println(a) // 运行结果为 1
}
不难发现副作用的产生往往与可变数据及共享状态有关,有时候它会使得结果变得难以预测。比如,我们在采用多线程处理高并发的场景,“并发访问”就是一个明显的例子。然而,在Kotlin编程中,我们推荐优先使用val来声明一个本身不可变的变量,这在大部分情况下更具有优势:
这是一种防御性的编码思维模式,更加安全和可靠,因为变量的值永远不会在其他地方被修改(一些框架采用反射技术的情况除外);
不可变的变量意味着更加容易推理,越是复杂的业务逻辑,它的优势就越大。
回到在Java中进行多线程开发的例子,由于Java的变量默认都是可变的,状态共享使得开发工作很容易出错,不可变性则可以在很大程度上避免这一点。当然,我们说过,val只能确保变量引用的不可变,那如何保证引用对象的不可变性?你会在第6章关于只读集合的介绍中发现一种思路。
- 点赞
- 收藏
- 关注作者
评论(0)