当我第一次学习Kotlin时,觉得val
和var
之间的区别似乎很简单,val
表示不可变,var
表示可变。
真相比这更微妙,val
不代表不可变,val
意味着只读。这意味着你不允许明确写入val
,但它不能保证它们是不可变的。[1]
原标题:Mutable vals in Kotlin
原文作者:Daniel Lew
原文地址:https://blog.danlew.net/2017/05/30/mutable-vals-in-kotlin/
可变类属性
对于变量,不可变和只读之间的区别是一个有争议的问题。没有办法编写val
变量或覆盖它的检索方式,因此(对于所有意图和目的)它是不可变的。
但是,对于类属性,只读属性val
会产生巨大的差异。
在属性的上下文中,val
与var
指示属性是否存在getter/setter。var
既有getter又有setter,而val
只有getter。
在简单的情况下,缺少setter意味着val
类属性是不可变的。但是,可以为任何类属性添加自定义getter函数,允许您在每次有人访问该属性时返回您想要的任何内容。例如:
class Person(val birthDay: DateTime) {
val age: Int
get() = yearsBetween(birthDay, DateTime.now())
}
如您所见,没有明确的设置方法Person.age
,但Person.age
会在当前日期更改时更改值。
实际上,将其Person.age
视为变量实际上是用词不当。它实际上是一个你正在调用的getter函数,它可能会改变调用值。
结论
当我在Kotlin学习可变vals时,我个人感到震惊。 我觉得被违背了val
与var
标记数据一样,不可变与可变是我学到的Kotlin的第一个很酷的功能之一!
据我所知,对于val
类属性,可定制的getter有两个参数:
- 类属性只是getter / setter的简写,并且通常认为定制getter是正常的。
- 可自定义的getter启用委派属性。
委托属性是一个引人注目的用例,我将继续使用它们。但是,我发现在val
暗示不可变引用时,更容易推理代码。不可变性使代码(尤其是并发代码)更容易使用。
因此,我选择不为val
类属性使用自定义getter 。如果只读类属性随时间改变值,我改为用正常函数替换该属性:
class Person(val birthDay: DateTime) {
fun age(): Int = yearsBetween(birthDay, DateTime.now())
}
这种偏好是Kotlin编码惯例所建议的。 它声明只有在基础算法时才应该优先考虑函数的属性:
- 不用throw
- 有复杂性
- 计算起来很低廉(或在第一次运行时计算)
- 通过调用返回相同的结果
覆盖getter并更改参考值会违反最后一个条件,因此请避免这样做!
[1]: 为了简洁起见,在这篇文章中,“不可变”实际上意味着“不可变参考”。 您始终可以引用可变对象(如ArrayList)。 在这种情况下防止可变性需要比简单使用val更先进的技术,我不会在这里讨论。