面试官:Kotlin中val可以修改指向的数组吗,var和val有什么区别?

2,147 阅读2分钟

在Kotlin实战中,可变变量和不可变变量是这么描述的

val (来自value)--- 不可变引用,使用val声明的变量不能在初始化之后再次赋值,对应的是Java的final变量。
var (来自variable)--- 可变引用,这种变量的值可以改变,对应的是普通的Java变量。

如何理解val的引用不可变的含义

val引用自身不可变

先看一段代码,当我们尝试给val声明的变量赋值的时候,编译器会报错,无法给val声明的变量赋值。由此可以说明,val引用自身是不可变的。

引用指向的变量可变

当val声明数组的时候,情况是什么样的呢?

可以看到,编译器依然会报错,但是如果仅仅修改其中一个元素的话,却是不一样的。

可以修改数组中的任意一个数值。由此可以看出,val指向的对象可能是可变的。 在kotlin核心编程中,也把val称之为variable final,即引用不可变。似乎更加确切一点。

总结

val声明的对象,引用本身不可变,引用指向的对象数据可以发生改变。

只读集合和可变集合

上文说到了val修饰的数组可以改变其中的值,Kotlin针对集合的是否改变将访问集合的数据接口和修改集合的数据接口进行了分离,也就是Collection 和 MutableCollection。

Collection

Collection 接口中只提供了 size,isEmpty(),contains()等方法,没有提供增删改等方法。

public interface Collection<out E> : Iterable<E> {
    public val size: Int
    public fun isEmpty(): Boolean
    public operator fun contains(element: @UnsafeVariance E): Boolean
    override fun iterator(): Iterator<E>
    public fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
}

MutableCollection

MutableCollection 继承自Collection,同时支持增加减少元素。

防御式拷贝

在Kotlin实战中,提到了这个概念,

就像 val 和 var 之间的分离一样,只读集合接口与可变集合接口的分离 能让程序 中的数据发生的事情更容易理解。如果函数接收 Collection 而不是 MutableCollection 作为形参,你就知道它不会修改集合,而只是读取集合中的 数据。 如何函数要求你传递给它 MutableCollection, 可以认为它将会修改数据。 如果你用了集合作为组件部状态的一部分,可能需要把集合先拷贝一份再传递给这 样的函数(这种模式通常称为防御式拷贝)。