Why Kotlin required put "==true" inside "if" verification with nullables var? - kotlin

folks!
We know, IF statement only works with true condition. The operator ? checks and guarantees firstName property is not null. So, if firstName isn't null and isEmpty() is also true, why "==true" is needed?
Shortly, why simple condition "firstName?.isEmpty()" is invalid?
class Person (firstName: String?) {
init {
if (firstName?.isEmpty() == true) {
println("firstName is null")
}else{
println("The name is $firstName")
}
}
}

The operator ? checks and guarantees firstName property is not null.
No, safe call operator doesn't guarantees this. It guarantees that you won't get NullPointerException if firstName is null, instead you will get null as a result of
firstName?.isEmpty() expression.
So, the type of this expression is Boolean?, and if condition must be Boolean.
I believe, you'd better use firstName.isNullOrEmpty() here.

It is because firstName?.isEmpty() can be true, false, or null.
if (null) does not make sense
UNLESS you are using a language that implicitly converts null to true or false.
Kotlin does not implicitly convert it.

IsEmpty will be called in this case only if firstName is not null

Do note that the code does not work as intended (i.e. prints null if it is null or empty)
https://pl.kotl.in/H7rBIT8H6
fun main() {
var firstName:String? = null
if(firstName?.isEmpty() ==true){
//print("firstName is null")
print("test1: TRUE. ") //<<-- THIS WILL NOT BE PRINTED
}else {
print ("test1: FALSE. ") // <<-- THIS IS RETURNED INSTEAD
}
firstName = ""
if(firstName?.isEmpty() ==true){
print("test2: TRUE. ") //<<--WORKS AS INTENDED
}else {
print ("test2: FALSE. ")
}
firstName = "Hello"
if(firstName?.isEmpty() ==true){
print("test1: TRUE. ")
}else {
print ("test1: FALSE. ") //<<--WORKS AS INTENDED
}
}
The output is
test1: FALSE. test2: TRUE. test1: FALSE.
notice "test1" is FALSE?
what is happening is the code
"firstName?.isEmpty()" returns null if firstName is null.
and consequently, "null == true" will return false
So instead, what you should do is either call
firstName.isNullOrEmpty()
or
firstName==null || firstName?.isEmpty()==true

The issue here is that your list is nullable. In Kotlin you should always avoid having nullable values. I would suggest refactoring.
Anyway, the answer is:
Comparison needed because the list is nullable and you need a fallback because if the list is null, isEmpty() will not be called so you will not get a boolean value or condition which is needed for if.
use isNullOrEmpty() instead

Related

Conditional max inside filter kotlin

I have a list, each element with the following fields:
active: true/false, optional_id: Int?, name: String
I am filtering for fields that have active true, to get the highest optional_id.
testList.filter { it.active == true }
.maxByOrNull { it.optional_id }
?. optional_id ?: 0
The idea is, if there are objects in the list, with the field active true, to get the highest optional_id amongst those.
Else, if they are all false (no active field), then return 0.
But since optional_id is of type Int?, maxByOrNull does not accept it without asserting !!. Is there any other way to handle null assertions for this scenario ?
I can't change the type of optional_id.
You can use maxOfWithOrNull with a nullsFirst comparator.
val highestOptionalId = testList.filter { it.active == true }
.maxOfWithOrNull(nullsFirst(naturalOrder())) { it.optionalId }
?: 0
"Of" suggests that it returns the selector's value, rather than the object in the list. "With" suggests that it uses a Comparator.
Is this what you are looking for?
testList.filter { it.active && it.optional_id != null }
.maxByOrNull { it.optional_id!! } ?.optional_id
?: 0
Following filter(...) you can safely assume (looks like you have no concurrency concerns) that optional_id is not null in maxByOrNull.

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 }) { ... }

Is it necessary to check null when use 'is' operator

I have an instance which can be null. Fox example
var str: String? = null
So I need to check if str is String. Do I need to check for null if I use the is operator.
First option:
if(str is String) {}
Second option:
if(str != null && str is String) {}
Please help me which way is better to use ?
The is operator is safe and returns false in the case you supply a null instance
https://pl.kotl.in/HIECwc4Av
Somewhere, you HAVE to nullcheck.
Kotlin provides many ways to enforce non-null:
Use a non-null type:
var nonNull : String = ""
var nullable : String? = "" // notice the ?
nullable = null // works fine!
nonNull = null // compiler error
and if you encounter a nullable type, you can use let {} ?: run {} construct to unwrap it and execute your code with a non-nullable:
nullable?.let { // use "it" to access the now non-null value
print(it)
} ?: run { // else
print("I am null! Big Sad!")
}
Kotlin strictly distinguishes between nullable T? and nonnull T.
Use T wherever possible to avoid null checks.

Kotlin - "If readLine() is null or empty assign default value, else parse readLine()"

I'm a beginner to Kotlin and am loving it so far. I'm curious if Kotlin has a quick and simple solution to checking user input/strings for emptiness. I'd like the funtionality of the following:
"If readLine() is null or empty assign default value, else parse readLine()"
And so far, what I've come up with is this:
var inp = readLine()
val double = if(inp.isNullOrEmpty()) 0.0 else inp.toDouble()
Is this the best it can be? I'd prefer to not store the original user input string if possible. Thanks for the help!
You can use toDoubleOrNull here:
val double: Double = readLine()?.toDoubleOrNull() ?: 0
If readLine() returns null, then the readLine()?.toDoubleOrNull() expression will also return null, and you'll fall back on the default value.
If readLine() returns a non-null value, toDoubleOrNull will attempt to parse it, and if it fails (for example, for an empty string), it will return null, making you fall back to the default once again.
Using the elvis operator and null-safe calls
fun main(args: Array<String>) {
val parsed = readLine()?.toDouble() ?: 0.0
}
Using ?. calls the method only if the value is not null, otherwise it just passes null along.
Using ?: means that the value on the left is returned if it is not null, otherwise the value on the right is returned
How about this solution:
System.console().readLine()?.ifBlank { null } ?: "default string value"
and for password (this doesn't work inside IntelliJ):
String(System.console().readPassword()).ifBlank { "password" }

How to negate a boolean expression when using the elvis operator in kotlin?

I want to negate the following expression:
return SpUtils.loadEMail()?.isEmpty() ?: false
If i add a ! before the expression, like
return !SpUtils.loadEMail()?.isEmpty() ?: false
The IDE(Android Studio) tells me
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type kotlin.Boolean?
How do I negate this kinds of expressions?
You have problem with nullable reference.
SpUtils.loadEMail()?.isEmpty()
This code produces value of type Boolean? that's mean expression can return an instance of Boolean or null.
I suggest following code to solve your problem:
return !(SpUtils().loadEMail()?.isEmpty() ?: false);
You trying negate Boolean? instead of Boolean, that elvis operator returns!
?. is a safe call operator.
In this case it returns you:
boolean value if the result of loadEmail() invocation is not null
null otherwise
! is a built-in boolean operation which invokes the package.Boolean's operator called not() which works only on non-nullable references. The result of ?. is Boolean?, thus you get your error.
If you want to negate the whole expression, you can stick to the iRus answer:
!(SpUtils().loadEMail()?.isEmpty() ?: false)
If you want to negate just the SpUtils().loadEMail()?.isEmpty() part then the correct variant will be:
!(SpUtils().loadEMail()?.isEmpty() ?: true)
If the result of ?. will be null (there is no mail) then the elvis operator will return you true (no e-mail) and you'll negate it.
!(SpUtils().loadEMail()?.isEmpty() ?: false) would work, but it's really difficult to see when this would actually return true.
It returns true when the result of SpUtils().loadEMail() is null or not empty. With this knowledge, we can easily make something readable:
return SpUtils().loadEMail()?.isNotEmpty() ?: true
You can expand it for even better readability:
val email = SpUtils().loadEMail()
if (email == null || email.isNotEmpty()) {
return true
}
return false