/**
* 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)
}
}
5 回答
来自标准库的Kotlin的List只读:
如上所述,还有MutableList
因此,Kotlin通过其接口强制执行只读行为,而不是像默认Java实现那样在运行时抛出异常 .
同样,有一个
MutableCollection
,MutableIterable
,MutableIterator
,MutableListIterator
,MutableMap
和MutableSet
,请参阅stdlib文档 .令人困惑但有三种,而不是两种不可变性:
Mutable - 你应该改变收藏品(Kotlin的
MutableList
)Readonly - 你不应该改变它(Kotlin的
List
),但有些东西可能(强制转换为Mutable,或者从Java改变)永恒 - 没有人可以改变它( Guava 的不可变集合)
因此,在情况(2)
List
只是一个没有变异方法的接口,但是如果将它转换为MutableList
,则可以更改实例 .使用Guava(案例(3)),即使使用演员或其他线程,您也可以安全地更改集合 .
为了直接使用Java集合,Kotlin选择了readonly,因此使用Java集合没有任何开销或转换 .
正如您在其他答案中看到的那样,Kotlin具有可变集合的只读接口,可让您通过只读镜头查看集合 . 但是可以通过转换或从Java操纵来绕过该集合 . 但是在合作的Kotlin代码中很好,大多数用法都不需要真正的不可变集合,如果你的团队避免使用可变形式的集合,那么你可能不需要完全不可变的集合 .
Kotlin集合允许复制变化突变以及惰性突变 . 因此,要回答部分问题,
filter
,map
,flatmap
,operator+
-
等所有内容都会在用于非惰性集合时创建副本 . 当在Sequence
上使用时,他们会在访问时将值修改为集合,并继续保持惰性(导致另一个Sequence
) . 虽然对于Sequence
,调用诸如toList
,toSet
,toMap
之类的任何内容都将导致最终副本生成 . 通过命名约定,几乎任何以to
开头的东西都在复制 .换句话说,大多数操作符都会返回与您开始时相同的类型,如果该类型是“只读”,那么您将收到一个副本 . 如果该类型是惰性的,那么您将懒惰地应用更改,直到您完全要求收集 .
有些人出于其他原因需要它们,例如并行处理 . 在这些情况下,最好是查看专为此目的而设计的高性能集合 . 并且只在这些情况下使用它们,而不是在所有一般情况下 .
在JVM世界中,很难避免与需要标准Java集合的库互操作,并且转换到这些集合或从这些集合转换会给不支持公共接口的库增加许多痛苦和开销 . Kotlin提供良好的互操作和缺乏转换,并通过 Contract 进行只读保护 .
因此,如果您无法避免需要不可变集合,Kotlin可以轻松处理JVM空间中的任何内容:
Guava (https://github.com/google/guava)
Dexx Scala集合到Java(https://github.com/andrewoma/dexx)的一个端口,带有Kotlin帮助程序(https://github.com/andrewoma/dexx/blob/master/kollection/README.md)
Eclipse Collections (以前称为GS-Collections)是一个非常高性能,JDK兼容,并行处理的顶级执行者,具有不可变和可变的变体(主页:https://www.eclipse.org/collections/和Github:https://github.com/eclipse/eclipse-collections)
PCollections (http://pcollections.org/)
此外,Kotlin团队正在为Kotlin本地开发Immutable Collections,可以在这里看到:https://github.com/Kotlin/kotlinx.collections.immutable
还有许多其他的收集框架可以满足所有不同的需求和限制,Google是您寻找它们的朋友 . Kotlin团队没有理由需要为其标准库重新发明它们 . 你有很多选择,他们专注于不同的事情,如性能,内存使用,非拳击,不变性等."Choice is Good" ...因此其他一些:HPCC,HPCC-RT,FastUtil,Koloboke,Trove以及......
甚至还有类似Pure4J的努力,因为Kotlin现在支持Annotation处理,也许可以为Kotlin提供类似理想的端口 .
Kotlin 1.0在标准库中不会有不可变的集合 . 但它确实具有只读和可变接口 . 没有什么能阻止你使用第三方不可变集合库 .
Kotlin的List接口"support only read-only access to the list"中的方法,而MutableList接口中的方法支持"adding and removing elements" . 然而,这两者都只是 interfaces .
Kotlin的
List
接口在编译时强制执行只读访问,而不是将此类检查推迟到运行时,如java.util.Collections.unmodifiableList(java.util.List)(“返回指定列表的不可修改视图... [where]尝试修改返回的列表...结果在UnsupportedOperationException . “_ 965660考虑以下Kotlin代码:
请注意如何
readOnlyList
是List
和方法,如add
无法解析(并不会编译),mutableList
可以自然突变,并add
上immutableList
(从谷歌 Guava ),也可以在编译时间内解决,但抛出一个异常,在运行 .所有上述断言都通过了最后一个断言,导致
Exception in thread "main" java.lang.AssertionError: Expected <[1, 2, 3, 4]>, actual <[1, 2, 3]>.
,即我们成功地改变了只读List
!请注意,使用
listOf(...)
而不是arrayListOf(...)
会返回一个有效的不可变列表,因为您无法将其强制转换为任何可变列表类型 . 但是,对变量使用List
接口不会阻止MutableList
分配给它(MutableList<E>
extendsList<E>
) .最后,请注意Kotlin(以及Java)中的接口不能强制实现不变性"cannot store state"(参见Interfaces) . 因此,如果您想要一个不可变的集合,您需要使用类似Google Guava提供的集合 .
另见ImmutableCollectionsExplained · google/guava Wiki · GitHub
NOTE: 这个答案就在这里,因为代码很简单,而且是开源的,您可以使用这个想法使您创建的集合不可变 . 它不仅仅是作为图书馆的广告 .
在Klutter library中,新的Kotlin Immutable包装器使用Kotlin委托来包装现有的Kotlin集合界面,保护层没有任何性能损失 . 然后,没有办法转换集合,它的迭代器或其他可能返回到可以修改的集合 . 它们变得无效 .
下面是代码中的一个示例,显示了该技术的简单性,通过将接口基本委托给实际类,同时覆盖变异方法,并且返回的任何子集合都是动态包装的 .
与帮助扩展功能一起使其易于访问:
您可以看到这个想法,并通过此代码创建缺少的类,重复其他引用类型的模式 . 或者在这里查看完整代码:
并且测试显示了之前允许修改的一些技巧,但现在没有,以及使用这些包装器的阻塞转换和调用 .