Is there any way to check if this obj is null-safe?


#1
import com.google.gson.Gson
import org.junit.Test

class ExampleUnitTest {
    @Test
    fun unsafe() {
        val a: A = Gson().fromJson("{}", A::class.java)
        check(a)
        println(a.s.length) // NullPointerException,
    }

    class A(val s: String)

    fun check(obj: Any) {
        if (/* Is there any way to assert the a.s is nonnull? */ true) {
            throw Exception("s is null")
        }
    }
}

#2

From your code I found you don’t understand how Kotlin deals with Platform Types.
Please read this: Null-Safety and Platform Types

The method fromJson's signature is

<T> T fromJson(String json, Class<T> typeOfT)

When you invoke it with Java code, you’ll get a type of T, which may be null.
If you invoke it with Kotlin code, you’ll get a type of T!, which may be null, too.
But Kotlin doesn’t allow us to use T! in code, you should use T? instead to tell Kotlin: It may be null.

// Use A? instead of A for null-safety
val a: A? = Gson().fromJson("{}", A::class.java)

// a may be null, so use safe-invoke operators
println(a?.s?.length)
// prints null

Remember, Anything from Java is null-dangerous, carefully check it.


#3

That’s not the point.
I want to check if a.s is null, not a,
and the fromJson method must return nonnull this case,


#4

Kotlin will have already checked that s is not null. If it was, it would have thrown an exception. Unless Gson does stupid reflection tricks that bypass the checks of Kotlin.

If you want to be able to check it yourself, change the type of s to String?.


#5

Gson use sun.misc.Unsafe bypass the checks of Kotlin.
then how to tell kotlin to check it again,


#6

You cannot. One way to handle this is to introduce an additional class:

import com.google.gson.Gson
import org.junit.Test

class ExampleUnitTest {
    @Test
    fun unsafe() {
        val unvalidatedA: UnvalidatedA = Gson().fromJson("{}", UnvalidatedA::class.java)
        val a = check(unvalidatedA)
        // Won't get here because "check(...)" will throw an exception.
        println(a.s.length)
    }

    class UnvalidatedA(val s: String?)
    class A(val s: String)

    fun check(unvalidatedA: UnvalidatedA) =
        A(unvalidatedA.s ?: throw Exception("s is null"))
}

#7

Not much graceful, but you could try java reflection to check the value of a.s.


#8

I would recommend to use Jackson instead. There you can tell library to create objects via constructors by applying for example: @ConstructorProperties or jackson-module-kotlin :slight_smile: