After knowing Kotlin, love the data class.
I could replace Java classes that has equal and hash and toString to it.
Most of these Java classes are serializable class. So my question is, when we convert to data class, do I still need to make it serializable explicitly? like
data class SomeJavaToKotlinClass(val member: String) : Serializable
Or it is okay to be
data class SomeJavaToKotlinClass(val member: String)
No, Kotlin data classes do not implicitly implement this interface. You can see from this example:
import java.io.Serializable
data class Foo(val bar: String)
fun acceptsSerializable(s: Serializable) { }
fun main(args: Array<String>) {
val f: Foo = Foo("baz")
acceptsSerializable(f) // Will not compile
}
I had to add : Serializable at the end of class to make is Serializable. Just like this
class SizeVariantModel (val price: Double, val discountedPrice: Double?) : Serializable
class ColorVariantModel (val name: String, val colorCode: String) : Serializable
I also had to import Serializable
import java.io.Serializable
Related
I am trying to use kotlinx #Serializable and Ive faced this issue:
I have the following classes:
#Serializable
sealed class GrandParent
a second one:
#Serializable
sealed class Parent() : GrandParent() {
abstract val id: String
}
and a third one
#Serializable
data class Child(
override val id: String, ....
): Parent()
I'm needing of grandparent since I use it as a generic type in another class, which happen to also have a reference to the GrandParent class
#Serializable
data class MyContent(
override val id: String,
....
val data: GrandParent, <- so it has a self reference to hold nested levels
...): Parent()
Every time I try to run this I get an error...
Class 'MyContent' is not registered for polymorphic serialization in the scope of 'GrandParent'.
Mark the base class as 'sealed' or register the serializer explicitly.
I am using ktor as wrapper, kotlin 1.5.10. I did this based on https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#registered-subclasses
Any ideas?
You should serialize and deserialize using your sealed class in order for kotlin serialization to "know" to add a discriminator with the right implementation. By default it search for type in the json but you can change it with JsonBuilder:
Json {
classDiscriminator = "class"
}
Here is an example:
#Serializable
sealed class GrandParent
#Serializable
sealed class Parent : GrandParent() {
abstract val id: String,
}
#Serializable
data class Child(
override val id: String,
): Parent()
#Serializable
data class MyContent(
override val id: String,
val data: GrandParent,
): Parent()
fun main() {
val test = MyContent(id = "test", data = Child(id = "child"))
val jsonStr = Json.encodeToString(GrandParent.serializer(), test)
println("Json string: $jsonStr")
val decoded = Json.decodeFromString(GrandParent.serializer(), jsonStr)
println("Decoded object: $decoded")
}
Result in console:
Json string: {"type":"MyContent","id":"test","data":{"type":"Child","id":"child"}}
Decoded object: MyContent(id=test, data=Child(id=child))
encode and decode can also be written like this (but behind the scenes it will use reflections):
val jsonStr = Json.encodeToString<GrandParent>(test)
println("Json string: $jsonStr")
val decoded = Json.decodeFromString<GrandParent>(jsonStr)
println("Decoded object: $decoded")
import kotlinx.serialization.Serializable
#Serializable
sealed class Exercise(open val id: String) {
#Serializable
data class Theory(override val id: String) : Exercise(id)
}
I have such kind of sealed class in my code, and compiler says me:
Serializable class has duplicate serial name of property 'id', either in the class itself or its supertypes.
Is there way to have open val in serializable sealed class, which works correctly when overriding it?
This is Kotlin issue KT-38958. It seems to be a corner case of the Constructor properties requirement.
It can be solved by using the following implementation,
import kotlinx.serialization.*
import kotlinx.serialization.json.Json
#Serializable
sealed class Exercise {
abstract val id: String
#Serializable
data class Theory(override val id: String) : Exercise()
}
fun main() {
val t1 = Exercise.Theory("t1")
val t1Json = Json.encodeToString(t1)
println(t1Json)
println(Json.decodeFromString<Exercise.Theory>(t1Json).toString())
}
which will output:
{"id":"t1"}
Theory(id=t1)
For details, see "Designing serializable hierarchy" in the Kotlin Serialization Guide.
I have 2 (data) classes that almost share the same properties:
data class Foo(
val id: FooId,
val name: String,
... 10+ properties
}
data class NewFoo(
val name: String,
... 10+ properties
}
I just want some syntax sugar magic here: not to repeat 10+ properties. I can make a base sealed class, but you would end up writing even more text (for passing arguments to base class ctor), although you are safer from making a mistake.
Yes, I know I could use composition for this, but here I don't want to, as there might be different 'variants' of the same data.
Am I missing something or this is not possible in Kotlin?
You can use an abstract (or sealed) class with abstract params instead and override them in the constructor of your data class (i.e. without additional passing them into the constructor of the base class).
abstract class Base {
// put commons parameter here
// abstract param needs to be initialized in the constructor of data class
abstract val name: String
// you can define some not-abstract params as well
open lateinit var someOtherParam: String
}
data class Foo1(
override val name: String,
val id: Int,
val someAdditionalParam1: String
) : Base()
data class Foo2(
override val name: String,
val someAdditionalParam2: String,
override var someOtherParam: String
) : Base()
I am trying to create a POJO (aka data classes in Kotlin) structure of a JSON response in Kotlin. I've implemented the Parcelable interface for each data class in the structure. In all of the data classes, I've auto generated the Parcelable implementation. The issue is the generated second constructor where the IDE is complaining about:
Overload resolution ambiguity
It states that it's being confused between these two constructors:
public constructor GeocodeRes(parcel: Parcel)
public constructor GeocodeRes(responset: ResponseRes)
Which I believe makes sense because ResponseRes is also of type Parcelable (ResponseRes implements Parcelable). So calling the GeocodeRes(parcel) method (within the createFromParcel companion method), it is getting confused.
That was until I removed ResponseRes from implementing the Parcelable class and it's still showing the same error.
Is there any reason to this? Am I setting this up properly? In all of the children data classes, they all implement the Parcelable interface (with dependence with eachother) but aren't running into any issues.
Here's my GeocodeRes class:
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
data class GeocodeRes(
#SerializedName("Response") #Expose val responset: ResponseRes
) : Parcelable {
// this is the problem. the IDE is complaining that the usage is too ambiguous (). however, the only usage of this constructor is within this class - just doesn't tell me where exactly.
constructor(parcel: Parcel) : this(parcel.readParcelable(ResponseRes::class.java.classLoader)) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeParcelable(responset, flags)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<GeocodeRes> {
override fun createFromParcel(parcel: Parcel): GeocodeRes {
return GeocodeRes(parcel)
}
override fun newArray(size: Int): Array<GeocodeRes?> {
return arrayOfNulls(size)
}
}
}
Here's my ResponseRes class:
data class ResponseRes(
#SerializedName("MetaInfo") #Expose val metaInfo: MetaInfo,
#SerializedName("View") #Expose val views: List<View>
): Parcelable
{
[...]//parcel methods
}
however, the only usage of this constructor is within this class - just doesn't tell me where exactly
The problem is with the definition itself, not with any usage. It could never be used, and the error would still be there.
You should be able to fix this by specifying which Parcelable you want to read:
this(parcel.readParcelable<ResponseRes>(ResponseRes::class.java.classLoader))
The compiler can't decide if you mean that or
this(parcel.readParcelable<Parcel>(ResponseRes::class.java.classLoader))
Even though the second wouldn't be legal because Parcel doesn't implement Parcelable, if you look at the signature
<T extends Parcelable> T readParcelable(ClassLoader loader)
you can see only the return type can be used to infer T, not the argument. So the compiler need to pick the constructor overload before trying to infer T.
In my Kotlin application I have a few entities (and a data class for each of them) and for each entity I have a service object implementing generic Service<T> interface.
I want to create a Factory of services that will return me a proper service based on a parameter which is a type of entity I want to have a service for. In Java I would pass a Class object into the factory which I could obtain from a static context of the entity class eg. Entity.class but I can't do that in Kotlin. How can I create a Factory that will produce me objects based on a type of an entity?
You're looking for KClass:
Say you have the following classes:
abstract class Parent(val name: String)
class ChildA : Parent("A")
class ChildB : Parent("B")
Then your factory may look like this:
fun <T : Any> factory(c: KClass<T>): T {
return c.createInstance()
}
fun main(args: Array<String>) {
val childA = factory(ChildA::class)
val childB = factory(ChildB::class)
println(childA.name) // A
println(childB.name) // B
}
But there's a better way using reified:
inline fun <reified T : Any> factory(): T {
return T::class.createInstance()
}
Then you can call it like this:
val childA = factory<ChildA>()
val childB = factory<ChildB>()
println(childA.name)
println(childB.name)
Note that without using reified we couldn't do T::class