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
,其中包含对 b1
、b2
和 b3
的引用修改 b1
、b2
,和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
}
我正在尝试简化 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
,其中包含对 b1
、b2
和 b3
的引用修改 b1
、b2
,和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
}