Null Type Checking with Elvis Return

A student in my CS1 course asked about the following example:

fun test(values: IntArray?): Int? {
   if (values?.isEmpty() ?: true) {
      return null
   }
   return values.min()
}

As shown when the code is loaded into the Kotlin Playground, the compiler errors here on the final line, claiming that values is still nullable. But it doesn’t seem like it can be, since null values will end up in the right side of the Elvis operator, entering the if and triggering the early return.

Rewrites of the code work as expected, such as:

fun test(values: IntArray?): Int? {
   if (values == null || values.isEmpty()) {
      return null
   }
   return values.min()
}

Curious if this is just a corner case in Kotlin’s null tracking or if we’re missing something more subtle here…

2 Likes

Putting this into IntelliJ yields a weak warning:

Equality check should be used instead of elvis for nullable boolean check

If we accept that refactoring, the code is changed to:

fun test(values: IntArray?): Int? {
  if (values?.isEmpty() != false) {
    return null
  }

  return values.min()
}

values is now properly smart casted and the error is no more:

image

As to why that works and the original does not: I guess the compiler just isn’t that smart here. You could try searching the issue tracker or create an issue there about this.

A note about style: both variants can be confusing to readers - there was also some discussion in an old thread about this. To make it clearer, you can replace the if block with

  if (values == null || values.isEmpty()) {
    return null
  }

which makes the intention immediately obvious and also produces the correct smart cast. To tidy this up a bit, you could also write an extension function isNullOrEmpty (see above mentioned thread for an example), so the code would become

  if (values.isNullOrEmpty()) {
    return null
  }

which couldn’t be clearer, really (I would probably do this).

And as a final remark: you can just use values?.minOrNull() (ref) if that’s all test does, no need to reinvent it. :slight_smile:

1 Like

I must admit, I’ve never quite understood the reason for that warning.

The equality check is a little more concise — but to me, it’s a little harder to see the ‘default’ truth value it assumes when there’s a null, something that’s much more obvious in the elvis version.

(But yes, I’ve added a few methods of my own along the lines of isNotNullOrEmpty(), which are concise, simple, and clear!)

1 Like