Just had evil gotcha

Consider this code

if(list?.isEmpty() ?: true) {
...
}

IDE for some reason really does not likes it and suggests it to convert to

if(list?.isEmpty() != false) {
...
}

Which works ok, but previous versions suggested wrong replacement like this

if(list?.isEmpty() == true) {
...
}

Had to review quite a bit of older code to fix this wrong old conversion. 63 occurrences so far.

How is that easier to understand than old fashioned way:

if(list != null && !list.isEmpty()) {
....        
}

I would use this, because think it is easier to read:

if (list != null && list.isNotEmpty()) {
    ...
}
if(dto?.subprop?.list?.isEmpty() != false) {
...
}

In that case, it does make sense. The code is much shorter.

But why doesn’t Kotlin convert null to false directly? It will save the weird tail: != false
Isn’t that much better?

if(dto?.subprop?.list?.isNotEmpty()) {
...
}

I get that, Boolean and Boolean? are two different types. So IDE will show compile error. But I really think it’s weird to have != false at the end.

I normally just have 2 utility functions for this

inline fun <A>Collection<A>?.isNullOrEmpty() = this == null || this.isEmpty()

inline fun <A>Collection<A>?.isNotNullOrEmpty() = this != null && this.isNotEmpty()

The problem with that function is that, currently, you cannot take advantage of smart cast to avoid the need to use safe calls (?.), so depending on the case, it may not make much sense to use it.

We can do this:

fun Boolean?.toBoolean() :Boolean {
    return this ?: false
}

if(dto?.subprop?.list?.isNotEmpty().toBoolean()) {
...
}

I wish Kotlin can automatically do the conversion for us, so that we don’t even need to write .toBoolean()

I think something like this is more elegant:

fun <T> T?.default(default: T): T = this ?: default


if(dto?.subprop?.list?.isNotEmpty().default(false)) {
...
}
1 Like

Great idea! Default function should be in standard library.
But still, I wish Kotlin will treat null as false where a boolean is needed, just like Go does.

Actually now that I think about it, it shouldn’t. Don’t know what I was thinking. We have default allready:

null ?: defualt

:man_facepalming:

That’s exactly op uses. But it’s not elegant to see true/false literals in a conditional expression, IMO

1 Like

I am also in disfavor of seeing true/false literals in conditional expressions

To me, list?.isEmpty() ?: true is much more readable than list?isEmpty() != false

It’s the double negation that irks me. Reading across you are in the flow of a nullable expression, and suddenly get twisted into a boolean expression of equivalence with a boolean literal where you have to manually work out the understanding of what happens to null instead of just reading the flow of null naturally.

1 Like

I actually created these and use them a lot :slightly_smiling_face:

fun Boolean?.isNullOrFalse() = this == null || this == false
fun Boolean?.isNullOrTrue() = this == null || this == true
fun Boolean?.isNotNullAndTrue() = this != null && this
fun Boolean?.isNotNullAndFalse() = this != null && !this

Use it like this:

selectedAction?.needsInput.isNotNullAndFalse()
2 Likes