Kotlin和不可变集合?
我正在学习Kotlin,看来我可能想在明年内把它作为我的主要语言。然而,我不断得到相互矛盾的研究,Kotlin有或没有不可变的集合,我试图弄清楚我是否需要使用谷歌番石榴 有人能给我一些指导吗?默认情况下是否使用不可变集合?哪些运算符返回可变集合或不可变集合?如果没有,是否有实施计划?标准库中的Kotlin是只读的:Kotlin和不可变集合?,kotlin,Kotlin,我正在学习Kotlin,看来我可能想在明年内把它作为我的主要语言。然而,我不断得到相互矛盾的研究,Kotlin有或没有不可变的集合,我试图弄清楚我是否需要使用谷歌番石榴 有人能给我一些指导吗?默认情况下是否使用不可变集合?哪些运算符返回可变集合或不可变集合?如果没有,是否有实施计划?标准库中的Kotlin是只读的: interface List<out E> : Collection<E> (source) 支持添加和删除的元素的常规有序集合 删除元素 参数 E-列表中
interface List<out E> : Collection<E> (source)
支持添加和删除的元素的常规有序集合
删除元素
参数E-列表中包含的元素类型 因此,Kotlin通过其接口强制执行只读行为,而不是像默认Java实现那样在运行时抛出异常
类似地,还有
MutableCollection
,MutableIterable
,MutableIterator
,MutableListIterator
,MutableMap
,和MutableSet
,请参见文档。这令人困惑,但有三种,而不是两种类型的不变性:
可变列表
)列表
),但可能会更改某些内容(转换为可变的,或从Java更改)List
只是一个没有变异方法的接口,但是如果将实例强制转换为MutableList
,则可以更改该实例
使用番石榴(案例(3))时,任何人都可以安全地更改收藏,即使使用石膏或其他线
Kotlin选择只读是为了直接使用Java集合,因此在使用Java集合时没有开销或转换。Kotlin 1.0在标准库中没有不可变的集合。但是,它确实具有只读和可变接口。并且没有任何东西阻止您使用第三方不可变集合库 Kotlin接口中的方法“仅支持对列表的只读访问”,而其接口中的方法支持“添加和删除元素”。然而,这两个都只是接口 Kotlin的
List
接口在编译时强制执行只读访问,而不是将此类检查延迟到运行时,如“返回指定列表的不可修改视图…[其中]尝试修改返回的列表…会导致错误”。它不强制执行不可变性。
考虑以下Kotlin代码:
import com.google.common.collect.ImmutableList
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
fun main(args: Array<String>) {
val readOnlyList: List<Int> = arrayListOf(1, 2, 3)
val mutableList: MutableList<Int> = readOnlyList as MutableList<Int>
val immutableList: ImmutableList<Int> = ImmutableList.copyOf(readOnlyList)
assertEquals(readOnlyList, mutableList)
assertEquals(mutableList, immutableList)
// readOnlyList.add(4) // Kotlin: Unresolved reference: add
mutableList.add(4)
assertFailsWith(UnsupportedOperationException::class) { immutableList.add(4) }
assertEquals(readOnlyList, mutableList)
assertEquals(mutableList, immutableList)
}
import com.google.common.collect.ImmutableList
导入kotlin.test.assertEquals
导入kotlin.test.assertFailsWith
趣味主线(args:Array){
val readOnlyList:List=arrayListOf(1,2,3)
val mutableList:mutableList=readOnlyList作为mutableList
val immutableList:immutableList=immutableList.copyOf(readOnlyList)
assertEquals(只读列表、可变列表)
assertEquals(可变列表、不可变列表)
//readOnlyList.add(4)//Kotlin:未解析的引用:add
可变列表。添加(4)
assertFailsWith(UnsupportedOperationException::class){immutableList.add(4)}
assertEquals(只读列表、可变列表)
assertEquals(可变列表、不可变列表)
}
请注意,readOnlyList
是一个列表
,诸如add
之类的方法无法解析(也不会编译),mutableList
可以自然变异,而immutableList
上的add
也可以在编译时解析,但在运行时抛出异常
以上所有断言都通过了,但最后一个断言除外,它导致线程“main”java.lang中出现异常。断言错误:预期,实际。
即,我们成功地变异了只读列表
请注意,使用listOf(…)
而不是arrayListOf(…)
返回一个有效的不可变列表,因为您无法将其强制转换为任何可变列表类型。但是,对变量使用list
接口并不会阻止将MutableList
分配给它(MutableList
扩展了List
)
最后,请注意,Kotlin(以及Java)中的接口不能强制执行不变性,因为它“不能存储状态”(请参阅)。因此,如果您想要一个不可变的集合,您需要使用类似于Google Guava提供的集合
另请参见正如您在其他答案中看到的,Kotlin具有可变集合的只读接口,允许您通过只读镜头查看集合。但是可以通过强制转换绕过集合或从Java操作集合。但是在协作Kotlin代码中,大多数使用不需要真正不变的集合,如果您的团队ds强制转换为集合的可变形式,那么您可能不需要完全不变的集合 Kotlin集合既允许更改时复制突变,也允许延迟突变。因此,为了回答您的部分问题,像
过滤器
,映射
,平面映射
,操作符+
-
都会在对非延迟集合使用时创建副本。当在序列
上使用时,它们会修改这些值在被访问时作为集合,并且继续是惰性的(导致另一个序列
)。虽然对于序列
,调用任何诸如toList
,toSet
,toMap
之类的命令都会生成最终副本。按照命名约定,几乎任何以to
开头的命令都会生成副本
换句话说,大多数操作符返回的类型与开始时相同,如果该类型为“readonly”,则您将收到一个副本。如果该类型为lazy,则您将收到一个副本
import com.google.common.collect.ImmutableList
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
fun main(args: Array<String>) {
val readOnlyList: List<Int> = arrayListOf(1, 2, 3)
val mutableList: MutableList<Int> = readOnlyList as MutableList<Int>
val immutableList: ImmutableList<Int> = ImmutableList.copyOf(readOnlyList)
assertEquals(readOnlyList, mutableList)
assertEquals(mutableList, immutableList)
// readOnlyList.add(4) // Kotlin: Unresolved reference: add
mutableList.add(4)
assertFailsWith(UnsupportedOperationException::class) { immutableList.add(4) }
assertEquals(readOnlyList, mutableList)
assertEquals(mutableList, immutableList)
}
/**
* Wraps a List with a lightweight delegating class that prevents casting back to mutable type
*/
open class ReadOnlyList <T>(protected val delegate: List<T>) : List<T> by delegate, ReadOnly, Serializable {
companion object {
@JvmField val serialVersionUID = 1L
}
override fun iterator(): Iterator<T> {
return delegate.iterator().asReadOnly()
}
override fun listIterator(): ListIterator<T> {
return delegate.listIterator().asReadOnly()
}
override fun listIterator(index: Int): ListIterator<T> {
return delegate.listIterator(index).asReadOnly()
}
override fun subList(fromIndex: Int, toIndex: Int): List<T> {
return delegate.subList(fromIndex, toIndex).asReadOnly()
}
override fun toString(): String {
return "ReadOnly: ${super.toString()}"
}
override fun equals(other: Any?): Boolean {
return delegate.equals(other)
}
override fun hashCode(): Int {
return delegate.hashCode()
}
}
/**
* Wraps the List with a lightweight delegating class that prevents casting back to mutable type,
* specializing for the case of the RandomAccess marker interface being retained if it was there originally
*/
fun <T> List<T>.asReadOnly(): List<T> {
return this.whenNotAlreadyReadOnly {
when (it) {
is RandomAccess -> ReadOnlyRandomAccessList(it)
else -> ReadOnlyList(it)
}
}
}
/**
* Copies the List and then wraps with a lightweight delegating class that prevents casting back to mutable type,
* specializing for the case of the RandomAccess marker interface being retained if it was there originally
*/
@Suppress("UNCHECKED_CAST")
fun <T> List<T>.toImmutable(): List<T> {
val copy = when (this) {
is RandomAccess -> ArrayList<T>(this)
else -> this.toList()
}
return when (copy) {
is RandomAccess -> ReadOnlyRandomAccessList(copy)
else -> ReadOnlyList(copy)
}
}