Search code examples
kotlincompiler-errorsstring-lengthnon-nullablekotlin-null-safety

Kotlin : if String is null and double bang(!!) operator is used, why does it give compile error for length function?


example:

fun main(){
    var userInput: String?
    //userInput = null
    userInput = "asbdef"
    var inputLength:Int? = userInput!!.length
    println("Length of the string is :"+inputLength)
}

Output : Length of the string is :6

fun main(){
    var userInput: String?
    userInput = null
    //userInput = "asbdef"
    var inputLength:Int? = userInput!!.length
    println("Length of the string is :"+inputLength)
}

Output : Unresolved reference: length

I want to know why it gives compile error?

If I just replace (!!) operator with (?) it compiles well but prints output as null.

PS: I'm newbie in Kotlin


Solution

  • The ?. operator short-circuits if the left side evaluates to null. So the result of nullVariable?.length is null and .length is never evaluated. Your first example is effectively doing:

    println("Length of the string is :" + null)
    

    The !! operator says "throw a NullPointerException if the left side is null. Otherwise, we know it's not null on the right side". You can see this if you change your second example a little:

    val userInput: String? = null
    val inputLength:Int = userInput!!.length // throws NullPointerException
    

    However, I'm not sure why are you are getting Unresolved reference: length. It looks like that the compiler is doing some optimization when you assign null directly to userInput, so rather than compiling it to a String? which throws an NPE at runtime, it knows the value is only null (not String?) at compile time, and therefore can't have the length property because null is not a reference.

    You can force it to give you to NullPointerException by adding a layer of abstraction via a function call:

    fun main(){
        var userInput: String? = "foo"
        userInput = alwaysNull()
        val inputLength:Int = userInput!!.length // NullPointerException
    }
    
    fun alwaysNull(): String? = null
    

    I don't see anything n the Null Safety documentation about the difference in behaviour between initializing in one line / via a function call vs. assigning null directly to a var though, so what's happening under the hood to get Unresolved reference is just a guess.