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

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.

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

How to use equals() and contains() to check nullable type of data? I suppose they are both string's methods, but why they behave differently?

CASE 1: it can compile and run. why no exception when null call equals() ?
var myStr:String? = null
if (myStr.equals("hello"))
println("equals hello")
else
println("not equals hello")
CASE 2: it cannot compile. I suppose it is similar to the above case, but I am wrong. Why?
var myStr:String? = null
if (myStr.contains("hello"))
println("contains hello")
else
println("not contains hello")
equals function is defined as extension function on nullable String reference String?, while contains method is defined on non-nullable CharSequence.
public actual fun String?.equals(other: String?, ignoreCase: Boolean = false): Boolean = ...
public operator fun CharSequence.contains(other: CharSequence, ignoreCase: Boolean = false): Boolean = ...
In both cases myStr is nullable String, so you cannot call contains directly. You can use null safety operator ?. for calling contains
if(myStr?.contains("hello") == true)
println("contains hello")
else
println("not contains hello")
PS: In case of equality check, you don't need to use equals method, instead you can just use == operator
equals on a nullable string works, only because it is a very special case. There is an equals specifically written for String?.
fun String?.equals(
other: String?,
ignoreCase: Boolean = false
): Boolean
This wouldn't work on Int?, for example:
var i: Int? = null
if (i.equals(1)) // error here
println("equals 1")
else
println("not equals 1")
The equals function is declared for Any, not Any?, so you can't call it on nullable types in general.
Anyway, the idiomatic way to compare equality is to use a == b, which translates to a?.equals(b) ?: (b === null) for a nullable a.
There is also no reason to allow myStr.contains("hello") to compile, since contains is declared on the non-nullable CharSequence.
operator fun CharSequence.contains(
other: CharSequence,
ignoreCase: Boolean = false
): Boolean
You can check it like this instead, with nullable chaining:
if (myStr?.contains("hello") == true)
In the first example, myStr.equals calls the String?.equals extension function, which does the following:
if (this === null)
return other === null
In your case, this is null, and other is not null, so other === null produces false.
In the second example, myStr.contains("hello") is trying to call a function called contains, but it doesn't exist, because you have a nullable String?, and there is no contains function defined for that type. There is the CharSequence.contains function, but that is only defined for non-nullable types.
So because the function doesn't exist, you get a compiler error.
Generally, you don't need to use the equals function anyway, and should prefer the == operator:
val myStr:String? = null
if (myStr == "hello")
println("equals hello")
else
println("not equals hello")
For contains, you can use the ?. operator to ensure the object on the left is not null first:
val myStr:String? = null
if (myStr?.contains("hello") == true)
println("contains hello")
else
println("not contains hello")
Here, myStr?.contains("hello") produces null, and null == true is false, so the result is false.

Kotlin : if String is null and double bang(!!) operator is used, why does it give compile error for length function?

example:
fun main(){
var userInput: String?
//userInput = null
userInput = "asbdef"
var inputLength:Int? = userInput!!.length
println("Length of the string is :"+inputLength)
}
Output :
Length of the string is :6
fun main(){
var userInput: String?
userInput = null
//userInput = "asbdef"
var inputLength:Int? = userInput!!.length
println("Length of the string is :"+inputLength)
}
Output :
Unresolved reference: length
I want to know why it gives compile error?
If I just replace (!!) operator with (?) it compiles well but prints output as null.
PS: I'm newbie in Kotlin
The ?. operator short-circuits if the left side evaluates to null. So the result of nullVariable?.length is null and .length is never evaluated. Your first example is effectively doing:
println("Length of the string is :" + null)
The !! operator says "throw a NullPointerException if the left side is null. Otherwise, we know it's not null on the right side". You can see this if you change your second example a little:
val userInput: String? = null
val inputLength:Int = userInput!!.length // throws NullPointerException
However, I'm not sure why are you are getting Unresolved reference: length. It looks like that the compiler is doing some optimization when you assign null directly to userInput, so rather than compiling it to a String? which throws an NPE at runtime, it knows the value is only null (not String?) at compile time, and therefore can't have the length property because null is not a reference.
You can force it to give you to NullPointerException by adding a layer of abstraction via a function call:
fun main(){
var userInput: String? = "foo"
userInput = alwaysNull()
val inputLength:Int = userInput!!.length // NullPointerException
}
fun alwaysNull(): String? = null
I don't see anything n the Null Safety documentation about the difference in behaviour between initializing in one line / via a function call vs. assigning null directly to a var though, so what's happening under the hood to get Unresolved reference is just a guess.

Not nullable value required to call 'component1()' function of destructuring declaration initializer

Is it possible to make the following code to compile in Kotlin?
val variable: String? = "string"
val (a, b) = variable?.run {
1 to 2
}
The compiler does not allow destructuring because the expression on the right-hand side is typed as a nullable Pair<Int, Int>?, and it's unclear what values a and b should get in case variable is null.
To solve this, you need to get a not-null expression after =.
There's a lot of different ways to deal with nullable values and produce a not-null value from a nullable one, see: In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them
For example, if you want to provide fallback values for a and b, then use the ?: operator as follows:
val (a, b) = variable?.run {
1 to 2
} ?: (0 to 0)
An alternative, for example, would be to check variable for null first:
val (a, b) = checkNotNull(variable) { "variable should never be null" }.run {
1 to 2
}
Null doesn't have any destructuring declarations. If you want a value of null to destructure like it's a pair of nulls, you could add these extensions:
operator fun <T> Pair<T, *>?.component1() = this?.component1()
operator fun <T> Pair<*, T>?.component2() = this?.component2()
Otherwise, as the other answer shows, you need to provide a default using the Elvis operator.
It's not automatic because it doesn't know what you want. Depending on what you're doing with it, 0 to 0 may be most appropriate, or maybe -1 to -1 or 0 to null or null to null.
The question is, what do you want to do if your variable is null? If you want to throw an exception, go with require or check as #hotkey suggested.
However I have the case where I just want to return if the value is null. So I wrote myself a little helper function that allows for this:
private inline fun <T> T?.exitIfNull(exitBlock: () -> Nothing): T {
if (this == null)
exitBlock()
else
return this
}
You can call this function as follows:
val (a, b) = variable?.run {
1 to 2
}.exitIfNull {
return
}
A nice little use of the Nothing keyword in Kotlin that I personally find quite fascinating

Filtering out non null values from a collection in kotlin

Take a look at this kotlin one liner:
val nonNullArr : List<NonNullType> = nullArray.filter {it != null}
The compiler gives a type error at this line, saying that a list of nullables can't be assigned to a list of non-nulls. But the filter conditional makes sure that the list will only contain non null values. Is there something similar to !! operator that I can use in this situation to make this code compile?
It seems logical to assume that the compiler would take into account the predicate
it != null
and infer the type as
List<NonNullType>
but it does not.
There are 2 solutions:
val nonNullList: List<NonNullType> = nullableArray.filterNotNull()
or
val nonNullList: List<NonNullType> = nullableArray.mapNotNull { it }
As far as I know, you cannot convert nullable types into nonNull types by just verifying that they are not null. To achieve what you want, you need to manually map nullable values to non-null type by simply creating NonNull type object. For this you can use map extension function.
val nullableArray: Array<String?> = arrayOf("abc", "xyz", null, "efg")
val nonNullList: List<String> = nullableArray.filter { it != null }.map {
it.toString()
}
Or you can use filterNotNull() method as #forpas suggested in comments
val nonNullList: List<String> = nullableArray.filterNotNull()
Hope it helps you!
You can't assign a nullable type to a non-nullable type of value.
The type-matching maybe works when you assign a value, not after filter operation called.
// the type-matching works before `.filter` is called
val nonNullArr : List<NonNullType> = nullArray//.filter {it != null}
instead, if you want to do this without an error or without concerning the type. Remove the type from the val, so it goes like this
val nonNullArr = nullArray.filter {it != null}
Hope it helps
try using listOfNotNull instead of listOf(), it is equivalent to list.filterNotNull()