0

I'm trying to simplify Parcelable code in Kotlin:

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() and readBoolean() are extension functions.

Is there a way to have a forEach on list with an assignable "it"?

Update: in a comment to one of the answers the author clarifies this question as:

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.

The intent is via the readonly list parcelBooleans which holds references to b1, b2, and b3 modify the values of b1, b2, and b3; but not mutating the list itself.

Jayson Minard
  • 84,842
  • 38
  • 184
  • 227
Geob-o-matic
  • 5,940
  • 4
  • 35
  • 41
  • Hi. Try this: http://stackoverflow.com/questions/5849154/can-we-write-our-own-iterator-in-java ( implement your own iterator: http://docs.oracle.com/javase/6/docs/api/java/util/Iterator.html ) – Totoo Apr 26 '15 at 10:53
  • You could create your own Iterator as an Extension-Method to List<>, e.g. forMutableEach { } – D3xter Apr 26 '15 at 11:10
  • This would be better if you wrote the question as a unit test with assertions so the intent is clear. Are you trying to update `b1`, `b2`, `b3` via their references in the list? Or are you just trying to updated the list values in `parcelBooleans` to new values. If the first, then the answer from Bashor. If the second, then the answer from me. You should update your question to be clear (I think reading every comment below helps, but that isn't very friendly) – Jayson Minard Jan 05 '16 at 09:11

5 Answers5

3

Update: KT-7033 has been fixed, this answer now works without requiring the kotlin-reflect.jar

I think simple way to achieve your wants is the using 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 will be fixed.

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
}

Another simple solution is using 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
}
Kirill Rakhman
  • 42,195
  • 18
  • 124
  • 148
bashor
  • 8,073
  • 4
  • 34
  • 33
  • I didn't know of mapVar delegate, this is neat :D I want to avoid the jar, as it is for Android. – Geob-o-matic Apr 27 '15 at 12:02
  • @Geobert -- This is indeed the best answer, https://youtrack.jetbrains.com/issue/KT-7033 was fixed so reflection jar should not be needed anymore. – Jayson Minard Jan 05 '16 at 09:23
0

NOTE: The question is not clear if the author is trying to mutate properties b1, b2, b3 via a reference within the list. Or if he is trying to modify the contents of list parcelBooleans which is a copy of the values from the properties. If the first, see answer from @Bashor (or just make the values of the list something who's sub-contents is mutable), if second, this answer is correct.

Given the extension functions defined in this other Stack Overflow question "how do you modify the contents of a list while iterating" you can change your code to simply:

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
}
Community
  • 1
  • 1
Jayson Minard
  • 84,842
  • 38
  • 184
  • 227
-1

A way to do it is to map each value:

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

You have to make parcelBooleans a mutable variable, though.

Egor Neliuba
  • 14,784
  • 7
  • 59
  • 77
  • 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. – Geob-o-matic Apr 26 '15 at 11:21
  • Just done a quick test: fun main(args: Array) { var b1: Boolean = false var b2: Boolean = false val bList = listOf(b1, b2) b1 = true println(bList) } – Geob-o-matic Apr 26 '15 at 11:27
  • @Geobert, you are right about `listOf` making a copy of your properties. This is true for pure Java too. – Egor Neliuba Apr 26 '15 at 11:29
  • Yes, not enough coffee this morning ^^; I'm going to wrap the needed basic types in a class with get/set method – Geob-o-matic Apr 26 '15 at 11:43
-1

Creating your own Iterator would be the OOP way to do it.

If you want to go the more functional way, you can use map(), e.g.

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

map() is similar to forEach, it uses the return value of each lambda execution to create a new List<>

D3xter
  • 6,165
  • 1
  • 15
  • 13
-2

Found out that my code at first can't work as the listOf makes a copy of the properties' value.

So I wrap the basic types in a simple class with get/set method:

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()) }
}
Geob-o-matic
  • 5,940
  • 4
  • 35
  • 41
  • I think You can simplify it to `class Box(var value: T)` – bashor Apr 26 '15 at 18:33
  • 1
    Other remarks: # instead to have two constructors You can use default arguments in primary constructor. # You can declare fields in primary constructors(see previous comment). # The making properties public is safe in Kotlin because it's not field like in Java and you can any time declare custom getters and setters. – bashor Apr 26 '15 at 18:41
  • I've done a generic MType yeah, but change all for the mapVar delegation solution – Geob-o-matic Apr 27 '15 at 15:07