Option 1
您可以使用.toList() http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/to-list.html要制作副本,尽管您将需要内部另一个属性来保存列表副本,或者您需要从数据类移至普通类。
作为数据类:
data class Notebook(private val _notes: List<String>) {
val notes: List<String> = _notes.toList()
}
这里的问题是你的数据类将有.equals()
and .hashCode()
基于潜在的变异列表。
所以替代方法是使用普通的类:
class Notebook(notes: List<String>) {
val notes: List<String> = notes.toList()
}
Option 2
Kotlin 团队也在致力于真正的不可变集合,如果它们足够稳定可以使用,您也许可以预览它们:https://github.com/Kotlin/kotlinx.collections.immutable https://github.com/Kotlin/kotlinx.collections.immutable
Option 3
另一种方法是创建一个允许后代类型的接口MutableList
要使用的。这正是杂乱的图书馆 https://github.com/kohesive/klutter通过创建一个轻量级委托类的层次结构来实现,该类可以包装列表以确保不可能发生突变。由于他们使用委托,因此几乎没有开销。您可以使用此库,或者仅查看源代码作为如何创建此类受保护集合的示例。然后您更改方法以请求此受保护版本而不是原始版本。请参阅源代码Klutter ReadOnly 集合包装器 https://github.com/kohesive/klutter/blob/master/core/src/main/kotlin/uy/klutter/core/collections/Immutable.kt and 相关测试 https://github.com/kohesive/klutter/blob/master/core/src/test/kotlin/uy/klutter/core/collections/TestImmutable.kt寻求想法。
作为使用这些类的示例Klutter https://github.com/kohesive/klutter,数据类将是:
data class Notebook(val notes: ReadOnlyList<String>) {
并且调用者将被迫通过传入一个包装列表来遵守,这非常简单:
val myList = mutableListOf("day", "night")
Notebook(myList.toImmutable()) // copy and protect
正在发生的事情是调用者(通过调用asReadOnly()
)正在制作防御性副本以满足您的方法的要求,并且由于这些类的设计方式,无法随后改变受保护的副本。
Klutter 实现中的一个缺陷是它没有单独的层次结构ReadOnly
vs. Immutable
所以如果调用者调用asReadOnly()
列表的持有者仍然可以引起突变。因此,在您的此代码版本(或 Klutter 的更新)中,最好确保所有工厂方法始终进行复制,并且绝不允许以任何其他方式构造这些类(即创建构造函数)internal
)。或者有第二个层次结构,在明确制作副本时使用。最简单的方法就是将代码复制到自己的库中,去掉asReadOnly()
方法只留下toImmutable()
并使集合类构造函数全部internal
.
更多信息
也可以看看: Kotlin 和不可变集合? https://stackoverflow.com/q/33727657/3679676