限制可变性(Limit mutability)
只读属性 val
只读属性val
可以改变,但是只读属性并没有提供修改的切入点,而修改切入点才是引起同步或者其他问题的主要原因。
尽管val
并不意味着“不可变”,例如我们可以通过自定义getter或者属性代理的方式定义val属性,这的确给了我们更多改变属性值的自由,但是在没有这些需求时,我们应该更倾向于使用final properties,Kotlin对其有更好的支持,例如smart cast:
val name: String? = "Márton"
val surname: String = "Braun"
val fullName: String?
get() = name?.let { "$it $surname" }
val fullName2: String? = name?.let { "$it $surname" }
fun main() {
if (fullName != null) {
println(fullName.length) // ERROR: Smart cast impossible
}
if (fullName2 != null) {
println(fullName2.length) // Márton Braun
}
}
区分只读、可变集合
Kotlin支持只读集合(read-only collections),Kotlin的只读集合是通过集合类的继承层次结构上的“有意”设计而达成的,具体说来就是,把集合类分为可变集合类和只读集合类,所有的可变集合类都继承自相应的只读集合类。
只读集合也不一定是不可变的,并且在底层实现上它们往往还真的就是可变集合,只是这些具体的实现被封装在只读接口之下,所以我们不能“轻易地”修改只读集合。例如,Iterable<T>.map
, Iterable<T>.filter
就是返回的ArrayList
,但是通过只读接口List
进行了屏蔽:
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
val destination = ArrayList<R>(collectionSizeOrDefault(10)); transform)
for (item in this)
destination.add(transform(item))
return destination
}
把只读集合设计成只读的而不是不可变的,给了我们更多的灵活性。在底层实现上,只要求具体的实现满足只读集合的接口即可,这样可以使用平台特定(platform-specific)的集合来实现Kotlin的只读集合(别忘了,Kotlin可是跨平台的语言)。
但是,希望Kotlin提供真正的不可变集合的呼声一直是络绎不绝,Kotlin也听到了大家的呼声,在kotlinx中提供了相应实现,具体参见kotlinx.collections.immutable,其中提供了不可变集合(Immutable collections)和持久化集合(Persistent collections)。
总结
我们应该限制可变性并且尽可能使用“不可变”对象,Kotlin提供了很多相关的工具,我们应该善于使用它们来限制修改切入点(mutation points):
- 更多地使用
val
而不是var
。 - 更多地使用不可变属性。
- 更多地使用不可变类/对象(data class)。
- 如果需要修改一个对象的属性,考虑使用data class并且使用
copy
。 - 当需要持有某种状态时,更多地使用只读集合而不是可变集合。
- 审慎地提供修改切入点,不要提供不必要的修改切入点。
- 不要对外暴露可变对象(mutable objects)。
以上规则当然会有例外,例如,我们可能会想使用可变对象来提高效率,当性能成为瓶颈的时候,的确可以考虑这么做,但是你应该知道,可变性意味着在多线程时需要格外的小心。使用可变对象从状态修改的角度看的确可以提升效率,但是如果叠加由此引发的多线程锁的开销,可变对象是否真的能提升效率就是一件值得考虑的事情了。总之,我们应该限制可变性。
从Immutability这一点其实可以看出编程语言的演进和软件工程的进化。Kotlin是“现代语言”中,选择拥抱Immutability的。
从设计上看,可变和不可变属性是平等的(var
和val
等长且很相似),甚至对于集合而言还更偏向于不可变集合(List
vs MutableList
),这些都是有意为之。相较而言,Java的可变和不可变就不是平等的(final vs 没有final),你或许会说Java更好啊,多个关键字更加的明确,但是扪心自问,是不是因为多个关键字你反而用的少得多。这不仅仅是个关键字的差别,表象背后体现的是编程哲学的不同。
更多内容可以查看这篇文章Immutability we can afford,作者Kotlin Leader。
Immutability is a luxury of programming, making software more comfortable and less error-prone to write in many important cases. Immutability is becoming more popular in software development because we became so rich in machine resources that we can afford it now.