Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin android parcelable

I've used auto generated feature of android plugin for android studio and it generated me the following code, but I can't understand why is there need for final val for CREATOR field ? I see final keyword for the first time in kotlin.

data class Person(
        val name: String,
        val surname: String
) : Parcelable {
    constructor(source: Parcel): this(source.readString(), source.readString())

    override fun describeContents(): Int {
        return 0
    }

    override fun writeToParcel(dest: Parcel?, flags: Int) {
        dest?.writeString(name)
        dest?.writeString(surname)
    }

    companion object {
        @JvmField final val CREATOR: Parcelable.Creator<Person> = object : Parcelable.Creator<Person> {
            override fun createFromParcel(source: Parcel): Person {
                return Person(source)
            }

            override fun newArray(size: Int): Array<Person?> {
                return arrayOfNulls(size)
            }
        }
    }
}
like image 728
ashur Avatar asked May 22 '16 09:05

ashur


3 Answers

In Kotlin classes and members are final by default. In other words the following declarations have the same bytecode:

@JvmField final val CREATOR: Parcelable.Creator<Person> = PersonCreator()
@JvmField val CREATOR: Parcelable.Creator<Person> = PersonCreator()

So while the generated code has final keyword and it is not an error it is redundant.

Even though classes and members are final by default there are still a need for final modifier in Kotlin. One example would be to mark open method as final in derived class:

open class Base {
    open fun test(){}
}

open class DerivedA : Base(){
    final override fun test(){}
}

class DerivedB : DerivedA() {
    override fun test(){} //error: 'test' in 'DerivedA' is final and cannot be overriden
}

While it's a good practice to make public static field final in java there's no strict requirement for the Parccelable.Creator field to be marked as such:

Classes implementing the Parcelable interface must also have a non-null static field called CREATOR of a type that implements the Parcelable.Creator interface.

like image 147
miensol Avatar answered Oct 02 '22 14:10

miensol


In Kotlin you can use the @Parcelize Kotlin Android Extension:

@Parcelize
data class User(val id: String, val name: String) : Parcelable

This is a compiler plugin that automatically generates the Parcelable implementation for you.

This page on the Kotlin docs gives more details about it, including requirements, supported types and how to create custom parcelers for unsupported types.


If you are curious and you want dive into the technical details of the implementation, see the Kotlin Evolution and Enhancement Process Compiler Extension to Support android.os.Parcelable.


This feature was experimental until Kotlin 1.3.40. If you are still using a Kotlin version earlier than 1.3.40, you need to enable the experimental features to use this:

android {
    // Enable @Parcelize
    // You only need this for Kotlin < 1.3.40
    androidExtensions {
        experimental = true
    }
    ...
}
like image 29
Albert Vila Calvo Avatar answered Oct 02 '22 16:10

Albert Vila Calvo


Use @Parcelize annotation to data class and extends Parcelable. Kotlin automatically do the rest for you. Example- Person data class.

@Parcelize
data class Person(val name: String, val age: Int, val email: String) : Parcelable

You can send value lets press on a button click like below from an activity.

   private val PERSON = "person"

   // first ensure a person object with data
   val person = Person("Shihab Uddin", 30, "[email protected]")

   binding.buttonSend.setOnClickListener {

        val intent = Intent(this, ReceiveParcelableAcitivty::class.java)
        //then put an parcelable extra to intent 
        intent.putExtra(PERSON, person)
        startActivity(intent)

    }

Receivers activity will get the data like

    private val PERSON = "person"
    intent?.let {
        var person = intent.extras.getParcelable(PERSON) as Person
        bind.textViewData.text = " Data Receive: $person"
    }

androidExtensions attribute is no more necessary < kotlin 1.3.40

android {

    // You only need this for Kotlin < 1.3.40
    androidExtensions {
        experimental = true
    }
    ...
}
like image 41
Shihab Uddin Avatar answered Oct 02 '22 14:10

Shihab Uddin