Operator call corresponds to a dot-qualified call 'List.min().compareTo(500)' which is not allowed on a nullable receiver 'List.min()' - kotlin

The following functions produces error. How to use let() or similar null check functions inside a if/for statement.
Here is my code:
fun main() {
var List = listOf<Int>(201, 53 ,5 ,556 ,70 , 9999)
var budget: Int = 500
if(List.min() < 500) { // this line produces the error
println("Yes you can buy from this shop")
}
}
And here is the error:
Operator call corresponds to a dot-qualified call 'List.min().compareTo(500)' which is not allowed on a nullable receiver 'List.min()'.
Help me with nullable types. Thank you

The question here is: what do you want to happen if your list is empty?
If a list has one or more items, and those items are comparable, then you can always find a minimum.  (It doesn't have to be unique.) But if the list has no items, then there is no minimum.  So what do you want to happen then?  Do you want to continue with a ‘default’ value?  Or skip that block?  Or something else?
If you want a default value, then you can use the elvis operator:
if ((list.minOrNull() ?: 0) < 500)
println("Yes you can buy from this shop")
That substitutes the value 0 if the list is empty.  (It doesn't have to be zero; any value will do.  In fact, this can work with any type as long as it's Comparable.)
Or you could do an explicit check for the list being empty:
if (list.isEmpty()) {
// Do something else
} else if (list.minOrNull()!! < 500)
println("Yes you can buy from this shop")
The !! non-null assertion operator works here, but it's a code smell.  (It's easy to miss when you're changing surrounding code; it could then throw a NullPointerException.)  So it's safer to handle the null.  Perhaps the most idiomatic way is with let():
list.minOrNull().let {
if (it == null) {
// Do something else
} else if (it < 500)
println("Yes you can buy from this shop")
}
(The < check is allowed there, because by that point the compiler knows it can't be null.)
Or if you just want to avoid the check entirely, use a ?. safe-call so that let() is only called on a non-null value:
list.minOrNull()?.let {
if (it < 500)
println("Yes you can buy from this shop")
}

Related

Kotlin idiom: null-safe conditional?

In Java, I'd write something like this:
if (foo != null && foo.bar()) { ...
However Kotlin complains that:
Smart cast to 'Foo' is impossible, because 'foo' is a mutable property that could have been changed by this time
I think it's saying what I wrote isn't thread-safe. However, in the context where I'm using it I know it is as this will always be invoked on a single thread.
Yes foo needs to be mutable. I realize that making foo a val would solve this but that is not possible here.
What's the correct idiom in Kotlin to handle this case?
In this case, a null-safe call returns a Boolean? so you can check if it equals true:
if (foo?.bar() == true) {
}
If you need non-null foo inside the conditional, then you can use the common ?.let idiom.
foo?.let { foo ->
if (foo.bar()) {
}
}
If you know it’s only accessed on this same thread, the !! operator would be safe after the null check, but ?.let is more idiomatic so easier to follow once you’re used to reading Kotlin.
The trick is to use Kotlin's excellent null-safety operators to avoid having to do redundant checks.
First, we use the safe-call operator.
foo?.bar()
This is a Boolean? (i.e. a nullable Boolean) which is null if foo is null, or the result of bar() if not. Now, a Boolean? is not a valid condition in an if statement, obviously, so we need to provide a "default" value of false. We do that using the amusingly-named Elvis operator
if (foo?.bar() ?: false) { ... }
If foo is null, then foo?.bar() is null, and ?: returns the value of the right-hand side, i.e. false. If foo is non-null, then foo?.bar() is the result of calling bar() on foo, and (assuming that result is also non-null), ?: returns the existing non-null Boolean value.
In case where the condition is not just a function call, e.g.
foo != null && foo.bar() > 0
you can use let or run:
if (foo.let { it != null && it.bar() > 0 }) { ... }
if (foo.run { this != null && bar() > 0 }) { ... }

Check for a value in Kotlin immutable list

I need to condense the following lines in kotlin to a more elegant way. I'm not able to figure out how to check the optional and the values at the same time. Basically I need to verify the list 'a' exists, has one or more items and that they are not 0.
val a = Utils.getItems() // returns an Optional<ImmutableList<ItemChange>>
if(!a.orElse(ImmutableList.of()).size > 0) {
val nonZero = a.get().filter { it.item != BigDecimal.ZERO }
return nonZero.size > 0
}
Assuming you also want to return false if non-existent or size 0, this is how I'd do it.
The any function returns true if any value matches, so it already takes care of the case of an empty list. And it breaks immediately if any match is found, whereas filter will exhaustively check the whole List and allocate a new List to hold the results.
Guava Optional can simply be converted to nullable with orNull() because Kotlin already has null-safety built in.
val items = Utils.getItems().orNull()
return items != null && items.any { it.item != BigDecimal.ZERO }

Kotlin's logical 'and' doesn't short-circuit?

I was following along Kotlin's documentation at http://kotlinlang.org/docs/reference/null-safety.html#checking-for-null-in-conditions and tried adapting this example,
val b = "Kotlin"
if (b != null && b.length > 0) {
print("String of length ${b.length}")
} else {
print("Empty string")
}
to the case where b = null. In an IntelliJ Idea Kotlin project I have an app.kt with a main() function defined as:
fun main() {
val b = null
if (b != null && b.length > 0) {
print("String of length ${b.length}")
} else {
print("Empty string")
}
}
However, when I run this, I get two compilation errors:
Information:Kotlin: kotlinc-jvm 1.3.20 (JRE 11+28)
Information:2019-02-02 15:07 - Compilation completed with 2 errors and 0 warnings in 1 s 921 ms
/Users/kurtpeek/IdeaProjects/HelloWorld/src/app.kt
Error:(3, 24) Kotlin: Unresolved reference: length
Error:(4, 37) Kotlin: Unresolved reference: length
I understand that the compiler is evaluating b.length even though the first condition, b != null, is false. This surprises me because I thought that the first check was to 'short-circuit' the Boolean expression if needed and make the call to b.length 'safe'.
For example, in Python, you can do this:
In [1]: "foo" == "bar" and what.the.heck
Out[1]: False
which works even though what is not defined, because the and 'stops' since "foo" is not equal to "bar".
Is this indeed how Kotlin works? It seems like missing Python's 'short-circuiting' feature would be a limitation.
Kotlin's && operator will short circuit (just like Java's) but only at runtime. What you are experiencing is a compile time error. The big difference to remember especially when comparing Kotlin (or Java) to Python is that Kotlin and Java are statically typed and have a compilation phase. So you'll get a compilation error if the types don't match up.
Let's go through these one at a time...
val b = "Kotlin"
if (b != null && b.length > 0) {
...
}
In this case, Kotlin will correctly infer that b is the type String, because you clearly set it to a String ("Kotlin"). We should note here that the String type cannot ever contain null. Knowing that, the b != null part of your if statement is unnecessary. However, after evaluating that (to true, always) it will evaluate b.length because b is a String and therefore has a length property. This example should compile fine (I didn't test it).
And next...
val b = null
if (b != null && b.length > 0) {
...
}
This code will not compile, let's go over why...
This code looks really similar but has one huge difference. In this case because you just set b to null, Kotlin is going to infer that b is an Nothing?. It has no information as to what type you want b to be, and you've set it to null (and because it's a val, it will always be null). Because b is null, it makes b nullable.
So, given that, when we compile b != null, that will always fail, because b can't ever be something that isn't null. But wait! We're compiling now... and when we run into b.length Kotlin will throw a compilation error because Nothing? does not have a length property!
Essentially, by setting b to null and not providing a type hint, Kotlin takes the only path it can to infer the type - Nothing?.
From your linked text: "Note that this only works where b is immutable (i.e. a local variable which is not modified between the check and the usage or a member val which has a backing field and is not overridable)".
val b=null is immutable, but since the type of null cannot be inferred nor stored, it cannot be used as the source in a valid shortcut.
If you changed to code to give it a nullable type, and set that null, this would work.

How to avoid !! in a function which returns a non-nullable

In the sample below, the function should return a non-null data.
Since the data could be changed in the process, it needs to be var, and can only be nullable to start with.
I can't use lateinit because the first call of if (d == null) will throw.
After the process it will be assigned a non-null data, but the return has to use the !! (double bang or non-null assertion operator).
What is the best approach to avoid the !!?
fun testGetLowest (dataArray: List<Data>) : Data {
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return d!!
}
If you don't like !! then supply a default value for it. You'll realize you can only supply the default value if the list is not empty, but, as you said, the list is already known to be non-empty. The good part of this story is that the type system doesn't track list size so when you say dataArray[0], it will take your word for it.
fun testGetLowest(dataArray: List<Data>) : Data {
var d: Data = dataArray[0]
for (i in 1 until dataArray.size) {
if (d.level < dataArray[i].level) {
d = dataArray[i]
}
}
return d
}
Normally, you can and should lean on the compiler to infer nullability. This is not always possible, and in the contrived example if the inner loop runs but once d is non-null. This is guaranteed to happen if dataArray has at least one member.
Using this knowledge you could refactor the code slightly using require to check the arguments (for at least one member of the array) and checkNotNull to assert the state of the dataArray as a post-condition.
fun testGetLowest (dataArray: List<Data>) : Data {
require(dataArray.size > 0, { "Expected dataArray to have size of at least 1: $dataArray")
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return checkNotNull(d, { "Expected d to be non-null through dataArray having at least one element and d being assigned in first iteration of loop" })
}
Remember you can return the result of a checkNotNull (and similar operators):
val checkedD = checkNotNull(d)
See Google Guava's Preconditions for something similar.
Even if you were to convert it to an Option, you would still have to deal with the case when dataArray is empty and so the value returned is undefined.
If you wanted to make this a complete function instead of throwing an exception, you can return an Option<Data> instead of a Data so that the case of an empty dataArray would return a None and leave it up to the caller to deal with how to handle the sad path.
How to do the same check, and cover the empty case
fun testGetLowest(dataArray: List<Data>)
= dataArray.minBy { it.level } ?: throw AssertionError("List was empty")
This uses the ?: operator to either get the minimum, or if the minimum is null (the list is empty) throws an error instead.
The accepted answer is completly fine but just to mentioned another way to solve your problem by changing one line in your code: return d ?: dataArray[0]

Kotlin: Cannot apply two conditionals at once check with "return body" function style

I have a simply function check like this:
fun parseValidBluetoothBrickedId(controllerId: String?): Boolean{
if(controllerId != null){
if(controllerId.startsWith(BLUETOOTH_NAME_PREFIX) && controllerId.length > BLUETOOTH_NAME_PREFIX.length)
return true
}
return false
}
I want to convert it to simplier style:
fun parseValidBluetoothBrickedId(controllerId: String?) =
controllerId?.length > BLUETOOTH_NAME_PREFIX.length
&& controllerId?.startsWith(BLUETOOTH_NAME_PREFIX)
but IDE(Android Studio 3.0 Beta7) gives me an error, underlines the greater than('>') operator:
Operator calls corresponds to a dot-qualified call 'controllerId?.length.compareTo(BLUETOOTH_NAME_PREFIX.length) which is not allowed here
Also it underline as an error line controllerId?.startsWith(BLUETOOTH_NAME_PREFIX) and says:
Type mismatch. Required: Boolean, Found Boolean?
What is the problem, really? It is just a simply method, works well with the first block if-else style.
You can't call compareTo (use the < operator) on controllerId?.length, since its type is Int?, which means it might be null, in which case it can't be compared as a number.
Similarly, the controllerId?.startsWith(BLUETOOTH_NAME_PREFIX) call returns Boolean? (since it will return null if controllerId is null), which can't be passed to the the && operator, only a real Boolean can.
The solution here is to do the null check that you were doing in your original method, and get rid of the safe calls by relying on smart casts casting your controllerId to String:
fun parseValidBluetoothBrickedId(controllerId: String?): Boolean =
controllerId != null
&& controllerId.length > BLUETOOTH_NAME_PREFIX.length
&& controllerId.startsWith(BLUETOOTH_NAME_PREFIX)
When you do controllerId?.length, you are getting an Int?. You cannot compare Int with an Int?. That is the reason you get the first error.
You get the other error because controllerId?.startsWith(BLUETOOTH_NAME_PREFIX) returns Boolean?. You cannot use the && operator on a nullable parameter. It requires two parameters of type Boolean.
To solve the problems, you need to check controllerId != null first. This will smart cast controllerId to the non-nullable type String. Like this:
fun parseValidBluetoothBrickedId(controllerId: String?): Boolean =
controllerId != null
&& controllerId.startsWith(BLUETOOTH_NAME_PREFIX)
&& controllerId.length > BLUETOOTH_NAME_PREFIX.length
Your transformed function is missing the null check:
fun parseValidBluetoothBrickedId(controllerId: String?) =
controllerId != null && controllerId.length > "".length
&& controllerId.startsWith("")
As in your first example, the null check is necessary for the compiler to know, that controllerId is not null. After the check, the compiler uses "smart casting" and the calls are safe.
Therefore you do not have to use ?. notation after the check.