Kotlin:可赋值 "it"?

Kotlin: assignable "it"?

我正在尝试简化 Kotlin 中的 Parcelable 代码:

public class C() : Parcelable {
    var b1: Boolean = false
    var b2: Boolean = false
    var b3: Boolean = false

    var i1: Int = 0
    var i2: Int = 0
    val parcelBooleans = listOf(b1, b2, b3)
    val parcelInts = listOf(i1, i2)

    override fun writeToParcel(p: Parcel, flags: Int) {
        parcelBooleans.forEach { p.writeBoolean(it) }
        parcelInts.forEach { p.writeInt(it) }
    }

    private fun readFromParcel(p: Parcel) {
         parcelBooleans.forEach{ it = p.readBoolean() } // does not compile as "it" is a "val"
    }
    // ... parcel creator bla bla
}

writeBoolean() 和 readBoolean() 是扩展函数。

有没有办法让列表中的 forEach 具有可分配的 "it"?

更新: 在对其中一个答案的评论中,作者将这个问题澄清为:

My point is that my list is not mutable, the elements inside are. Elements are my properties. But I'm realizing that maybe, listOf is making a value copy of my properties… so either I must rely on reflection, or wrapping basic type into an class to allow change to their value.

目的是通过只读列表 parcelBooleans,其中包含对 b1b2b3 的引用修改 b1b2,和b3;但不改变列表本身。

一种方法是映射每个值:

parcelBooleans = parcelBooleans.map{ p.readBoolean() }

不过,您必须使 parcelBooleans 成为可变变量。

创建您自己的迭代器将是实现它的 OOP 方式。

如果你想要更实用的方式,你可以使用 map(),例如

private fun readFromParcel(p: Parcel) {
     val mappedBooleans = parcelBooleans.map{ p.readBoolean() }
}

map() 类似于forEach,它使用每个lambda执行的return值来创建一个新的List<>

发现我的代码起初无法工作,因为 listOf 复制了属性值。

所以我用 get/set 方法将基本类型包装在一个简单的 class 中:

public class MBoolean() {
var internal: Boolean = false

constructor(b: Boolean) : this() {
    internal = b
}

fun set(b: Boolean) {
    internal = b
}

fun get(): Boolean {
    return internal
}
}

public class MInt() {
private var internal: Int = 0

constructor(i: Int) : this() {
    set(i)
}

public fun set(i: Int) {
    internal = i
}

public fun get(): Int {
    return internal
}
}

override fun writeToParcel(p: Parcel, flags: Int) {
    parcelBooleans.forEach { p.writeBoolean(it.get()) }
    parcelInts.forEach { p.writeInt(it.get()) }
}

private fun readFromParcel(p: Parcel) {
    parcelBooleans.forEach { it.set(p.readBoolean()) }
    parcelInts.forEach { it.set(p.readInt()) }
}

更新: KT-7033 已修复,此答案现在无需 kotlin-reflect.jar

我认为实现您的需求的简单方法是使用 property references, but note that than you should provide kotlin-reflect.jar in classpath (until KT-7033 not fixed). Additionally it may be simplified after KT-6947 将被修复。

public class C_by_Reflection() : Parcelable {
    var b1: Boolean = false
    var b2: Boolean = false
    var b3: Boolean = false

    var i1: Int = 0
    var i2: Int = 0
    val parcelBooleans = listOf(::b1, ::b2, ::b3)
    val parcelInts = listOf(::i1, ::i2)

    override fun writeToParcel(p: Parcel, flags: Int) {
        parcelBooleans.forEach { p.writeBoolean(it.get(this)) }
        parcelInts.forEach { p.writeInt(it.get(this)) }
    }

    private fun readFromParcel(p: Parcel) {
         parcelBooleans.forEach{ it.set(this, p.readBoolean()) }
    }
    // ... parcel creator bla bla
}

另一个简单的解决方案是使用 delegated properties:

public class C_by_Delgates() : Parcelable {
    val mapBooleans = hashMapOf<String, Any?>()
    var b1: Boolean by mapBooleans 
    var b2: Boolean by mapBooleans 
    var b3: Boolean by mapBooleans 

    val mapInts = hashMapOf<String, Boolean>()
    var i1: Int by mapInts 
    var i2: Int by mapInts 

    override fun writeToParcel(p: Parcel, flags: Int) {
        mapBooleans.forEach { p.writeBoolean(it.value as Boolean) }
        mapInts.forEach { p.writeInt(it.value as Int) }
    }

    private fun readFromParcel(p: Parcel) {
        mapBooleans.forEach { mapBooleans[it.key] = p.readBoolean() }
    }
    // ... parcel creator bla bla
}

注意: 问题不清楚作者是否试图改变属性b1 , b2, b3 通过列表中的引用。或者,如果他试图修改列表 parcelBooleans 的内容,该列表是属性值的副本。如果是第一个,请参阅@Bashor 的答案(或者只是使列表的值成为 sub-contents 可变的东西),如果是第二个,则此答案是正确的。

鉴于在另一个 Stack Overflow 问题 中定义的扩展函数,您可以将代码简单地更改为:

public class C() : Parcelable {
    var b1: Boolean = false
    var b2: Boolean = false
    var b3: Boolean = false

    var i1: Int = 0
    var i2: Int = 0

    // CHANGED:  use mutable list, array, or primitive array
    val parcelBooleans = arrayListOf(b1, b2, b3) 

    val parcelInts = listOf(i1, i2)

    override fun writeToParcel(p: Parcel, flags: Int) {
        parcelBooleans.forEach { p.writeBoolean(it) }
        parcelInts.forEach { p.writeInt(it) }
    }

    private fun readFromParcel(p: Parcel) {
        // CHANGED:  using the extension function
        parcelBooleans.mapInPlace { p.readBoolean() }
    }
    // ... parcel creator bla bla
}