可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm currently using the excellent AutoParcel in my Java project, which facilitates the creation of Parcelable classes.
Now, Kotlin, which I consider for my next project, has this concept of data classes, that automatically generate the equals, hashCode and toString methods.
Is there a convenient way to make a Kotlin data class Parcelable in a convenient way (without implementing the methods manually)?
回答1:
Kotlin 1.1.4 is out
Android Extensions plugin now includes an automatic Parcelable implementation generator. Declare the serialized properties in a primary constructor and add a @Parcelize annotation, and writeToParcel()/createFromParcel() methods will be created automatically:
@Parcelize
class User(val firstName: String, val lastName: String) : Parcelable
So you need to enable them adding this to you module's build.gradle:
apply plugin: 'org.jetbrains.kotlin.android.extensions'
android {
androidExtensions {
experimental = true
}
}
回答2:
You can try this plugin:
android-parcelable-intellij-plugin-kotlin
It help you generate Android Parcelable boilerplate code for kotlin's data class. And it finally look like this:
data class Model(var test1: Int, var test2: Int): Parcelable {
constructor(source: Parcel): this(source.readInt(), source.readInt())
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeInt(this.test1)
dest?.writeInt(this.test2)
}
companion object {
@JvmField final val CREATOR: Parcelable.Creator<Model> = object : Parcelable.Creator<Model> {
override fun createFromParcel(source: Parcel): Model{
return Model(source)
}
override fun newArray(size: Int): Array<Model?> {
return arrayOfNulls(size)
}
}
}
}
回答3:
Have you tried PaperParcel? It's an annotation processor which automatically generates the Android Parcelable
boilerplate code for you.
Usage:
Annotate your data class with @PaperParcel
, implement PaperParcelable
, and add a JVM static instance of the generated CREATOR
e.g.:
@PaperParcel
data class Example(
val test: Int,
...
) : PaperParcelable {
companion object {
@JvmField val CREATOR = PaperParcelExample.CREATOR
}
}
Now your data class is Parcelable
and can be passed directly to a Bundle
or Intent
Edit: Update with latest API
回答4:
The best way with no boilerplate code at all is Smuggler gradle plugin. All you need is just implement AutoParcelable interface like
data class Person(val name:String, val age:Int): AutoParcelable
And that's all. Works for sealed classes as well. Also this plugin provides compile time validation for all AutoParcelable classes.
UPD 17.08.2017 Now with Kotlin 1.1.4 and Kotlin Android extensions plugin you could use @Parcelize
annotation. In this case, the example above will look like:
@Parcelize class Person(val name:String, val age:Int): Parcelable
No need for data
modifier. The biggest downside, for now, is using kotlin-android-extensions plugin which has a lot of other functions that could be unnecessary.
回答5:
Just click on the data keyword of your kotlin data class, then press alt+Enter, select the first option saying "Add Parceable Implementation"
回答6:
I will leave my way of doing in case it might help someone.
What i do is i have a generic Parcelable
interface DefaultParcelable : Parcelable {
override fun describeContents(): Int = 0
companion object {
fun <T> generateCreator(create: (source: Parcel) -> T): Parcelable.Creator<T> = object: Parcelable.Creator<T> {
override fun createFromParcel(source: Parcel): T = create(source)
override fun newArray(size: Int): Array<out T>? = newArray(size)
}
}
}
inline fun <reified T> Parcel.read(): T = readValue(T::class.javaClass.classLoader) as T
fun Parcel.write(vararg values: Any?) = values.forEach { writeValue(it) }
And then i create parcelables like this:
data class MyParcelable(val data1: Data1, val data2: Data2) : DefaultParcelable {
override fun writeToParcel(dest: Parcel, flags: Int) { dest.write(data1, data2) }
companion object { @JvmField final val CREATOR = DefaultParcelable.generateCreator { MyParcelable(it.read(), it.read()) } }
}
Which gets me rid of that boilerplate override.
回答7:
Unfortunately there is no way in Kotlin to put a real field in an interface, so you can't inherit it from an interface-adapter for free:
data class Par : MyParcelable
You may look at delegation, but it does not help with fields, AFAIK: https://kotlinlang.org/docs/reference/delegation.html
So the the only option I see is a fabric function for Parcelable.Creator
, which is kind of obvious.
回答8:
Using Android Studio and the Kotlin plugin, I found an easy way to convert my old Java Parcelable
s with no extra plugins (if all you want is to turn a brand new data
class into a Parcelable
, skip to the 4th code snippet).
Let's say you have a Person
class with all the Parcelable
boiler plate:
public class Person implements Parcelable{
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
private final String firstName;
private final String lastName;
private final int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
protected Person(Parcel in) {
firstName = in.readString();
lastName = in.readString();
age = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(firstName);
dest.writeString(lastName);
dest.writeInt(age);
}
@Override
public int describeContents() {
return 0;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
}
Start by stripping out the Parcelable
implementation, leaving a bare-bones, plain, old Java object (properties should be final and set by the constructor):
public class Person {
private final String firstName;
private final String lastName;
private final int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
}
Then let the Code > Convert Java file to Kotlin File
option do its magic:
class Person(val firstName: String, val lastName: String, val age: Int)
Convert this into a data
class:
data class Person(val firstName: String, val lastName: String, val age: Int)
And finally, let's turn this into a Parcelable
again. Hover the class name and Android Studio should give you the option to Add Parcelable Implementation
. The result should look like this:
data class Person(val firstName: String, val lastName: String, val age: Int) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.readInt()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(firstName)
parcel.writeString(lastName)
parcel.writeInt(age)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Person> {
override fun createFromParcel(parcel: Parcel): Person {
return Person(parcel)
}
override fun newArray(size: Int): Array<Person?> {
return arrayOfNulls(size)
}
}
}
As you can see, the Parcelable
implementation is some auto-generated code appended to you data
class definition.
Notes:
- Trying to convert a Java
Parcelable
directly into Kotlin will not produce the same result with the current version of the Kotlin plugin (1.1.3
).
- I had to remove some extra curly braces the current
Parcelable
code generator introduces. Must be a minor bug.
I hope this tip works for you as well as it did for me.
回答9:
i prefer just using the https://github.com/johncarl81/parceler lib with
@Parcel(Parcel.Serialization.BEAN)
data class MyClass(val value)
回答10:
There is a Plugin but is not always as updated as Kotlin is evolving:
https://plugins.jetbrains.com/plugin/8086
Alternative:
I have a working example of a custom data class using Parcelable
and lists:
Data classes using Parcelable with Lists:
https://gist.github.com/juanchosaravia/0b61204551d4ec815bbf
Hope it helps!
回答11:
Kotlin has made the entire process of Parcelization in Android damn easy with its @Parcel annotation.
To do that
Step 1. Add Kotlin extensions in your app module gradle
Step 2. Add experimental = true since this feature is still in experimentation in gradle.
androidExtensions {
experimental = true
}
Step 3. Annonate the data class with @Parcel
Here is an simple example on @Parcel usage
回答12:
Kotlin has made the entire process of Parcelization in Android damn easy with its @Parcel annotation.
To do that
Step 1. Add Kotlin extensions in your app module gradle
Step 2. Add experimental = true since this feature is still in experimentation in gradle.
androidExtensions {
experimental = true
}
Step 3. Annonate the data class with @Parcel
Here is a simple example on @Parcel usage in Kotlin to pass data between Android Components.