Search code examples
kotlinif-statementlogical-operatorsnullablekotlin-null-safety

Null check with logical operator 'and' throws error while '&&' doesn't in Kotlin


The difference between && and and and the difference between || and or:

  • && and || use short-circuit evaluation while
  • and and or always evaluate every condition

Kotlin doc for "and"

But apart from that, I get different behaviour for the following example:

Using &&, this snippet perfectly works:

var str1: String? = "Good Morning"
var str2: String? = "How's it going"

if (str1 != null && str2 != null) {
    println(str1.length + str2.length)
}

When I replace the && with and I have to add parenthesis around both conditions, otherwise the compiler seems to get confused because I get the error: Operator '!=' cannot be applied to 'String?' and 'BigInteger'

Using and and adding parenthesis:

var str1: String? = "Good Morning"
var str2: String? = "How's it going"

if ((str1 != null) and (str2 != null)) {
    println(str1.length + str2.length)
}

But this last snippet throws:

Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

Shouldn't both expressions do precisely the same? Evaluate both conditions and only execute the println when both String values are non-null? What is the difference?


Solution

  • && is different from and in an additional way in that it introduces a smart cast from String? to String, allowing you to access length by saying str1.length and str2.length, rather than using str1?.length and str2?.length.

    From the Kotlin language specification:

    Smart casts are introduced by the following Kotlin constructions.

    • Conditional expressions (if)
    • When expressions (when);
    • Elvis operator (operator ?:);
    • Safe navigation operator (operator ?.);
    • Logical conjunction expressions (operator &&);
    • Logical disjunction expressions (operator ||);
    • Not-null assertion expressions (operator !!);
    • Cast expressions (operator as);
    • Type-checking expressions (operator is);
    • Simple assignments;
    • Platform-specific cases: different platforms may add other kinds of expressions which introduce additional smart cast sources.

    You need the extra parenthesis because and, being an infix function, has a higher precedence than &&, which is not an infix function. See all the precedences here.