Use ?: with something like Int.MIN_VALUE, Int.MAX_VALUE, Long.MIN_VALUE, Long.MAX_VALUE for some limited data types. We need to be careful when calculating on min/max values to avoid overflow.
However, I am not happy with these approaches. Or maybe I don’t know an existing ideal solution?
What if Kotlin supports these operators
a ?< b nulls first less than
a ?> b nulls first greater than
a ?<= b nulls first less than or equal
a ?>= b nulls first greater than or equal
a <? b nulls last less than
a >? b nulls last greater than
a <=? b nulls last less than or equal
a >=? b nulls last greater than or equal
Maybe we don’t need the nulls first operators if by default we consider <, >, <=, >= as nulls first, to be consistent with compareValues function. But I am afraid that it’s not backward compatible and could break existing code, where we don’t know how null value is currently handled by implementer.
I think if compiler see these operators, it could automatically apply the logic below (or similar) for us:
// T?.compareTo(other: T?) for nulls first operators
if (a === b) return 0
if (a == null) return -1
if (b == null) return 1
return a.compareTo(b) // reuse current non-nullable compareTo()
// T?.compareTo(other: T?) for nulls last operators
if (a === b) return 0
if (a == null) return 1
if (b == null) return -1
return a.compareTo(b) // reuse current non-nullable compareTo()
In the original proposal above, if ? is the left most char, it means nullsFirst, regardless of the next chars.
Below are the alternative operators to my original proposal:
a ?< b compare with null, consider null is less than non-null value, then if both are not null check a < b
a >? b compare with null, consider null is less than non-null value, then if both are not null check a > b
a ?<= b compare with null, consider null is less than non-null value, then if both are not null check a <= b
a >=? b compare with null, consider null is less than non-null value, then if both are not null check a >= b
a <? b compare with null, consider null is greater than non-null value, then if both are not null check a < b
a ?> b compare with null, consider null is greater than non-null value, then if both are not null check a > b
a <=? b compare with null, consider null is greater than non-null value, then if both are not null check a <= b
a ?>= b compare with null, consider null is greater than non-null value, then if both are not null check a >= b
In the alternative proposal, if ? is on the left of <, it means, null is considered as smaller than non-null values.
Not sure which way is better for readers. For me, the original proposal is easier for my eyes because I don’t need to check the relationship between ? and < / >.
Actually, I think, it’s still backward compatible because otherwise the existing code should not compilable. But always having ? is more clear about a nullable comparison and explicitly about nulls first.
You probably know this already, but if you often use such operations in your project, you can create infix extension with a nullable receiver:
fun test(x1: Int?, x2: Int?) {
if (x1 nullsFirstLT x2) {
}
}
infix fun <T : Comparable<T>> T?.nullsFirstLT(other: T?): Boolean = when {
this === other -> false
this == null -> true
other == null -> false
else -> this < other
}
// alternatively: = this !== other && other != null && (this == null || this < other)
@broot Hi, thanks for your suggestion!
Yes, I have all cases as function extensions in my projects. But I am still not really happy with it.
Reading code with nice operators look more natural I think.