我使用Google的Guava库为可变列表提供了一个自定义的getter方法,以返回一个不可接受的列表。然后在构造函数中访问这个可变列表。
data class mutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo>
get() = ImmutableList.copyOf(field)
init {
mutableList = mutableListOf()
list.forEach {
mutableList.add(it.copy()) // Exception is thrown here.
// It actually calls its getter method which is an immutable
// list, so when init this class, it throw exception
}
}
}
data class Foo {}
我把它反编译成Java,在初始化代码块中,它调用了mutableList的getter方法。有没有办法调用mutabbleList本身而不是getter方法?
发布于 2018-06-02 02:16:54
当然,它会调用getter (返回ImmutableList.copyOf(field)
)。
您可以在init
块中简单地对新复制的可变列表进行mutableList
赋值:
data class MutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo>
get() = ImmutableList.copyOf(field)
init {
mutableList = list.map { it.copy() }.toMutableList()
}
}
或者没有init
data class MutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo> = list.map { it.copy() }.toMutableList()
get() = ImmutableList.copyOf(field)
}
发布于 2019-09-18 21:34:45
Kotlin stdlib选择了接口的不变性。这意味着,封装实现的接口决定了引用本身的可变性。
因此,使MutableList<T>
成为List<T>
的正确方法是将其装箱,如下所示:
val myMutableList = mutableListOf(1, 2, 3, 4)
val myImmutableList = myMutableList as List<Int>
这样,作为List<Int>
中的myImmutableList
引用,它将只公开来自List<Int>
的成员,而不是那些仅由MutableList<Int>
定义的成员,这些成员允许改变对象的状态,因此也就是列表。
然后,如果你真的想避免下面的问题(从上面的代码恢复),
val hackedList = myImmutableList as MutableList<Int>
..。对于您可以通过取消装箱访问可变实现的情况,您可能会选择以下解决方案:
class ImmutableList<T>(list: MutableList<T>) : List<T> by list
fun <T> MutableList<T>.toImmutable() = ImmutableList(this)
然后按如下方式使用它:
val myMutableList = mutableListOf(1, 2, 3, 4)
val myImmutableList = myMutableList.toImmutable()
因此,您将避免上面的问题。实际上,任何对从MutableList<T>.toImmutable()
返回的值进行拆箱的尝试都将以TypeCastException
结束,因为List<T>
的实现不再是MutableList<T>
。相反,它是一个ImmutableList<T>
,它不会公开任何可能改变对象的方法。
与@Lucas方法不同,这种方法不会浪费时间复制元素,因为您将依赖Kotlin中的by
关键字,它允许您通过已存在的实现来实现接口。也就是说,您将传递给ImmutableList<T>
的构造函数的MutableList<T>
。
发布于 2021-04-19 14:48:48
对我来说,使用var而不是val字段和一个私有setter通常效果最好
class Order
class Something() {
var orders: List<Order> = listOf()
private set
fun addOrder(order: Order) {
orders = orders
.toMutableList()
.apply { add(order) }
}
}
这将它公开为不可变的,并且只需要一个字段。我们付出的代价是在添加元素时创建一个新集合的开销。
https://stackoverflow.com/questions/50648717
复制相似问题