Kotlin not nullable value can be null?

2019-04-04 18:55发布

问题:

I have backend that return me some json.

I parse it to my class:

class SomeData(
  @SerializedName("user_name") val name: String,
  @SerializedName("user_city") val city: String,
  var notNullableValue: String)

Use gson converter factory:

Retrofit retrofit = new Retrofit.Builder()
  .baseUrl(ENDPOINT)
  .client(okHttpClient)
  .addConverterFactory(GsonConverterFactory.create(gson))
  .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
  .build();

And in my interface:

interface MyAPI {
    @GET("get_data")
    Observable<List<SomeData>> getSomeData();
}

Then I retrieve data from the server (with rxJava) without any error. But I expected an error because I thought I should do something like this (to prevent GSON converter error, because notNullableValue is not present in my JSON response):

class SomeData @JvmOverloads constructor(
  @SerializedName("user_name") val name: String,
  @SerializedName("user_city") val city: String,
 var notNullableValue: String = "")

After the data is received from backend and parsed to my SomeData class with constructor without def value, the value of the notNullableValue == null.

As I understand not nullable value can be null in Kotlin?

回答1:

Yes, that is because you're giving it a default value. Ofcourse it will never be null. That's the whole point of a default value.

Remove ="" from constructor and you will get an error.

Edit: Found the issue. GSON uses the magic sun.misc.Unsafe class which has an allocateInstance method which is obviously considered very unsafe because what it does is skip initialization (constructors/field initializers and the like) and security checks. So there is your answer why a Kotlin non-nullable field can be null. Offending code is in com/google/gson/internal/ConstructorConstructor.java:223

Some interesting details about the Unsafe class: http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/



回答2:

Try to override constructor like this:

class SomeData(
        @SerializedName("user_name") val name: String,
        @SerializedName("user_city") val city: String,
        var notNullableValue: String = "") {
    constructor() : this("","","")
}

Now after server response you can check the notNullableValue is not null - its empty