If I have a nullable type Xyz?
, I want to reference it or convert it to a non-nullable type Xyz
. What is the idiomatic way of doing so in Kotlin?
For example, this code is in error:
val something: Xyz? = createPossiblyNullXyz()
something.foo() // Error: "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Xyz?"
But if I check null first it is allowed, why?
val something: Xyz? = createPossiblyNullXyz()
if (something != null) {
something.foo()
}
How do I change or treat a value as not null
without requiring the if
check, assuming I know for sure it is truly never null
? For example, here I am retrieving a value from a map that I can guarantee exists and the result of get()
is not null
. But I have an error:
val map = mapOf("a" to 65,"b" to 66,"c" to 67)
val something = map.get("a")
something.toLong() // Error: "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Int?"
The method get()
thinks it is possible that the item is missing and returns type Int?
. Therefore, what is the best way to force the type of the value to be not nullable?
Note: this question is intentionally written and answered by the author (Self-Answered Questions), so that the idiomatic answers to commonly asked Kotlin topics are present in SO. Also to clarify some really old answers written for alphas of Kotlin that are not accurate for current-day Kotlin.
Best Answer
First, you should read all about Null Safety in Kotlin which covers the cases thoroughly.
In Kotlin, you cannot access a nullable value without being sure it is not
null
(Checking for null in conditions), or asserting that it is surely notnull
using the!!
sure operator, accessing it with a?.
Safe Call, or lastly giving something that is possiblynull
a default value using the?:
Elvis Operator.For your 1st case in your question you have options depending on the intent of the code you would use one of these, and all are idiomatic but have different results:
For the "Why does it work when null checked" read the background information below on smart casts.
For your 2nd case in your question in the question with
Map
, if you as a developer are sure of the result never beingnull
, use!!
sure operator as an assertion:or in another case, when the map COULD return a null but you can provide a default value, then
Map
itself has agetOrElse
method:Background Information:
Note: in the examples below I am using explicit types to make the behavior clear. With type inference, normally the types can be omitted for local variables and private members.
More about the
!!
sure operatorThe
!!
operator asserts that the value is notnull
or throws an NPE. This should be used in cases where the developer is guaranteeing that the value will never benull
. Think of it as an assert followed by a smart cast.read more: !! Sure Operator
More about
null
Checking and Smart CastsIf you protect access to a nullable type with a
null
check, the compiler will smart cast the value within the body of the statement to be non-nullable. There are some complicated flows where this cannot happen, but for common cases works fine.Or if you do a
is
check for a non-nullable type:And the same for 'when' expressions that also safe cast:
Some things do not allow the
null
check to smart cast for the later use of the variable. The example above uses a local variable that in no way could have mutated in the flow of the application, whetherval
orvar
this variable had no opportunity to mutate into anull
. But, in other cases where the compiler cannot guarantee the flow analysis, this would be an error:The lifecycle of the variable
nullableInt
is not completely visible and may be assigned from other threads, thenull
check cannot be smart cast into a non-nullable value. See the "Safe Calls" topic below for a workaround.Another case that cannot be trusted by a smart cast to not mutate is a
val
property on an object that has a custom getter. In this case, the compiler has no visibility into what mutates the value and therefore you will get an error message:read more: Checking for null in conditions
More about the
?.
Safe Call operatorThe safe call operator returns null if the value to the left is null, otherwise continues to evaluate the expression to the right.
Another example where you want to iterate a list but only if not
null
and not empty, again the safe call operator comes in handy:In one of the examples above we had a case where we did an
if
check but have the chance another thread mutated the value and therefore no smart cast. We can change this sample to use the safe call operator along with thelet
function to solve this:read more: Safe Calls
More about the
?:
Elvis OperatorThe Elvis operator allows you to provide an alternative value when an expression to the left of the operator is
null
:It has some creative uses as well, for example throw an exception when something is
null
:or to return early from a function:
read more: Elvis Operator
Null Operators with Related Functions
Kotlin stdlib has a series of functions that work really nicely with the operators mentioned above. For example:
Related Topics
In Kotlin, most applications try to avoid
null
values, but it isn't always possible. And sometimesnull
makes perfect sense. Some guidelines to think about:in some cases, it warrants different return types that include the status of the method call and the result if successful. Libraries like Result give you a success or failure result type that can also branch your code. And the Promises library for Kotlin called Kovenant does the same in the form of promises.
for collections as return types always return an empty collection instead of a
null
, unless you need a third state of "not present". Kotlin has helper functions such asemptyList()
oremptySet()
to create these empty values.when using methods which return a nullable value for which you have a default or alternative, use the Elvis operator to provide a default value. In the case of a
Map
use thegetOrElse()
which allows a default value to be generated instead ofMap
methodget()
which returns a nullable value. Same forgetOrPut()
when overriding methods from Java where Kotlin isn't sure about the nullability of the Java code, you can always drop the
?
nullability from your override if you are sure what the signature and functionality should be. Therefore your overridden method is morenull
safe. Same for implementing Java interfaces in Kotlin, change the nullability to be what you know is valid.look at functions that can help already, such as for
String?.isNullOrEmpty()
andString?.isNullOrBlank()
which can operate on a nullable value safely and do what you expect. In fact, you can add your own extensions to fill in any gaps in the standard library.assertion functions like
checkNotNull()
andrequireNotNull()
in the standard library.helper functions like
filterNotNull()
which remove nulls from collections, orlistOfNotNull()
for returning a zero or single item list from a possiblynull
value.there is a Safe (nullable) cast operator as well that allows a cast to non-nullable type return null if not possible. But I do not have a valid use case for this that isn't solved by the other methods mentioned above.