In Kotlin, similar to default values in functions, we can initialize the constructor parameters with some default values.

If the class is initialized with arguments passed, those arguments are used as parameters. However, if the class is initialized without passing argument(s), default arguments are used.

class A (p1:String,p2:String = "P2")

You can initialize the class in two different ways:

val a1 = A("x","y") // p1 = x, p2 = y

val a2 = A("m") // p1 = m, p2 = P2

So far so good.

The real problem comes when you deserialize a JSON let’s say after an API call using Retrofit. To demonstrate the deserialization process, I’ll be using Gson Library instead of making an actual API call.

Let’s check out some scenarios. Try this code in Kotlint REPL. If you are not familiar with Kotlin REPL, then here is a great article that you can refer to. Let’s declare classes in 8 different ways :



("p1") val p1: String? = "P1",

("p2") val p2: String? = "P2"

) class A( @SerializedName ("p1") val p1: String? = "P1", @SerializedName ("p2") val p2: String? = "P2"

("p1") val p1: String? = "P1",



) class B( @SerializedName ("p1") val p1: String? = "P1", @SerializedName ("p2") val p2: String?

("p1") val p1: String? = "P1",

("p2") val p2: String? = "P2"

) data class C( @SerializedName ("p1") val p1: String? = "P1", @SerializedName ("p2") val p2: String? = "P2"

("p1") val p1: String? = "P1",

("p2") val p2: String?

) data class D( @SerializedName ("p1") val p1: String? = "P1", @SerializedName ("p2") val p2: String?

("p1")

val p1: String? = "P1"

("p2")

val p2: String? = "P2"

} class E { @SerializedName ("p1")val p1: String? = "P1" @SerializedName ("p2")val p2: String? = "P2"

("p1")

var p1: String? = "P1"

("p2")

var p2: String? = "P2"

} class F { @SerializedName ("p1")var p1: String? = "P1" @SerializedName ("p2")var p2: String? = "P2"

("p1")

val p1: String? = "P1",

("p2")

val p2: String? = "P2"

) data class G( @SerializedName ("p1")val p1: String? = "P1", @SerializedName ("p2")val p2: String? = "P2"

("p1")

val p1: String?,

("p2")

val p2: String? = "P2"

) data class H( @SerializedName ("p1")val p1: String?, @SerializedName ("p2")val p2: String? = "P2"

Let’s pass empty JSON string {} to Gson and deserialize it to their respective classes.

val gson = Gson()

print("

")

println("A : " + gson.toJson(gson.fromJson("{}",A::class.java)))

print("

")

println("B : " + gson.toJson(gson.fromJson("{}",B::class.java)))

print("

")

println("C : " + gson.toJson(gson.fromJson("{}",C::class.java)))

print("

")

println("D : " + gson.toJson(gson.fromJson("{}",D::class.java)))

print("

")

println("E : " + gson.toJson(gson.fromJson("{}",E::class.java)))

print("

")

println("F : " + gson.toJson(gson.fromJson("{}",F::class.java)))

print("

")

println("G : " + gson.toJson(gson.fromJson("{}",G::class.java)))

print("

")

println("H : " + gson.toJson(gson.fromJson("{}",H::class.java)))

print("

")

Now bring a pen and paper and write down what you think the output will be and then compare it with the actual output.

Here is what it’ll be the output. Have you got all of these right?

A : {}

B : {}

C : {}

D : {}

E : {"p1":"P1","p2":"P2"}

F : {"p1":"P1","p2":"P2"}

G : {}

H : {}

Whoa!!. What just happened… Where all the default parameters go. Is it a bug in Kotlin or Gson ?

Explanation:

No. It’s the way Gson is implemented.

Default arguments inside the class constructor are ignored in every scenario except E AND F . This is especially awful when we consider the assumption of null-safety by the developer when using non-nullable types. It will result in a NullPointerException at runtime with no hints by the IDE about possible nullability. We won’t even get an exception while parsing, because Gson uses unsafe reflection and Java has no concept of the non-nullable types.

Gson is intended for usage with pure Java and doesn’t interpret Kotlin primary constructors properly.

If there is a default/no-argument constructor, it is called, otherwise, Gson calls no constructor at all. If you add init{} block for all the classes, then it’ll be called only for class E and F in this case.

A constructor that has no parameter is known as default constructor. If we don’t define a constructor in a class, then compiler creates default constructor(with no arguments) for the class. And if we write a constructor with arguments or no-arguments then the compiler does not create a default constructor.

Then Gson sets the property values (also, it bypasses actual setters of Kotlin properties and sets the fields directly, which may sometimes lead to unexpected behavior).

Solution: