我有一个关于 Kotlin 集合的一般性问题。
MutableList
当我们有val
vsvar
区别时,为什么会有这么多集合的可变版本(比如)?
好吧....好吧...实际上,我知道这val
与对象的“可变性”无关,而是与对象的“可重新初始化”有关。
但这提出了一个问题....为什么不是MutableList
默认值?
我有一个关于 Kotlin 集合的一般性问题。
MutableList
当我们有val
vsvar
区别时,为什么会有这么多集合的可变版本(比如)?
好吧....好吧...实际上,我知道这val
与对象的“可变性”无关,而是与对象的“可重新初始化”有关。
但这提出了一个问题....为什么不是MutableList
默认值?
单独地,可变和不可变集合能够公开在单个接口中不能共存的有用功能:
List
作为示例。Kotlin 中的不可变集合不能添加或删除元素;它们只能从中读取。但是这种明显的限制使得对不可变集合进行一些子类型化成为可能。来自 Kotlin 文档:
只读集合类型是协变的……集合类型与元素类型具有相同的子类型关系。
这意味着,如果一个Rectangle
类是一个类的子Shape
类,您可以在需要时将一个List<Rectangle>
对象放入一个List<Shape>
变量中:
fun stackShapes(val shapesList: List<Shape>) {
...
}
val rectangleList = listOf<Rectangle>(...)
// This is valid!
stackShapes(rectangleList)
另一方面,可变集合可以读取和写入。正因为如此,它们不可能有子类型或超类型。来自 Kotlin 文档:
...可变集合不是协变的;否则,这将导致运行时失败。如果
MutableList<Rectangle>
是 的子类型MutableList<Shape>
,则可以将其他 Shape 继承者(例如 Circle)插入其中,从而违反其 Rectangle 类型参数。
val rectangleList = mutableListOf<Rectangle>(...);
val shapesList: MutableList<Shape> = rectangleList // MutableList<Rectangle>-type object in MutableList<Shape>-type variable
val circle = Circle(...)
val shape: Shape = circle // Circle-type object in Shape-type variable
// Runtime Error!
shapesList.add(shape) // You're actually trying to add a Circle to a MutableList<Rectangle>
// If rectanglesList couldn't be put into a variable with type MutableList<Shape> in the first place, you would never have run into this problem.
此时,您可能会想:“那又怎样?Kotlin 可以只为可变集合的所有写入方法添加类型检查……然后您可以允许它们是协变的,并且您不需要单独的不可变收藏!”
这是真的,只是它会完全违背 Kotlin 的核心哲学;尽可能避免nulls
和运行时错误。您会看到,每当类型检查失败时,此类 Collection 的方法都必须返回null
- 或引发异常。这只会在运行时变得明显,因为可以通过简单地使可变集合保持不变来避免这种情况......这正是 Kotlin 所做的。
来自 Kotlin 文档:
只读集合类型是协变的。这意味着,如果 Rectangle 类继承自 Shape,您可以在
List<Rectangle>
任何List<Shape>
需要的地方使用 a。换句话说,集合类型与元素类型具有相同的子类型关系。映射在值类型上是协变的,但在键类型上不是。
反过来,可变集合不是协变的。否则,这将导致运行时失败。如果
MutableList<Rectangle>
是 的子类型MutableList<Shape>
,则可以将其他 Shape 继承者(例如 Circle)插入其中,从而违反其 Rectangle 类型参数。
换句话说,如果它是不可变的,那么您就知道所有类型都是相同的。如果没有,您可能有不同的继承人。