CASE 1: it can compile and run. why no exception when null call equals() ?

var myStr:String? = null
if (myStr.equals("hello"))  
    println("equals hello")
else
    println("not equals hello")

CASE 2: it cannot compile. I suppose it is similar to the above case, but I am wrong. Why?

var myStr:String? = null
if (myStr.contains("hello"))
    println("contains hello")
else
    println("not contains hello")
3

There are 3 answers

0
sidgate On BEST ANSWER

equals function is defined as extension function on nullable String reference String?, while contains method is defined on non-nullable CharSequence.

public actual fun String?.equals(other: String?, ignoreCase: Boolean = false): Boolean = ...


public operator fun CharSequence.contains(other: CharSequence, ignoreCase: Boolean = false): Boolean = ...

In both cases myStr is nullable String, so you cannot call contains directly. You can use null safety operator ?. for calling contains

if(myStr?.contains("hello") == true)
    println("contains hello")
else
    println("not contains hello")

PS: In case of equality check, you don't need to use equals method, instead you can just use == operator

0
Sweeper On

equals on a nullable string works, only because it is a very special case. There is an equals specifically written for String?.

fun String?.equals(
    other: String?, 
    ignoreCase: Boolean = false
): Boolean

This wouldn't work on Int?, for example:

var i: Int? = null
if (i.equals(1)) // error here
    println("equals 1")
else
    println("not equals 1")

The equals function is declared for Any, not Any?, so you can't call it on nullable types in general.

Anyway, the idiomatic way to compare equality is to use a == b, which translates to a?.equals(b) ?: (b === null) for a nullable a.

There is also no reason to allow myStr.contains("hello") to compile, since contains is declared on the non-nullable CharSequence.

operator fun CharSequence.contains(
    other: CharSequence, 
    ignoreCase: Boolean = false
): Boolean

You can check it like this instead, with nullable chaining:

if (myStr?.contains("hello") == true)
1
Adam Millerchip On

In the first example, myStr.equals calls the String?.equals extension function, which does the following:

if (this === null)
    return other === null

In your case, this is null, and other is not null, so other === null produces false.


In the second example, myStr.contains("hello") is trying to call a function called contains, but it doesn't exist, because you have a nullable String?, and there is no contains function defined for that type. There is the CharSequence.contains function, but that is only defined for non-nullable types.

So because the function doesn't exist, you get a compiler error.


Generally, you don't need to use the equals function anyway, and should prefer the == operator:

val myStr:String? = null
if (myStr == "hello")
    println("equals hello")
else
    println("not equals hello")

For contains, you can use the ?. operator to ensure the object on the left is not null first:

val myStr:String? = null
if (myStr?.contains("hello") == true)
    println("contains hello")
else
    println("not contains hello")

Here, myStr?.contains("hello") produces null, and null == true is false, so the result is false.