Search code examples
androidkotlinkotlin-null-safety

What is difference between !! and ? in kotlin's null safety?


I am a bit confused about the usage of ? and !! in the following instance.

  lat = mLastLocation?.latitude.toString()
  longi = mLastLocation!!.longitude.toString()

Which null-safety operator should I be using?


Solution

  • TL;DR:

    ?. operator is safe. Use it when you are unsure about the chain nullability.
    !!. operator is meant to be used only when you are sure that the previous chain operation result is not null. Otherwise, crash.

    if mLastLocation is never null, feel safe about using !!. (And about rethinking a little bit your code), otherwise, use ?.

    Introduction

    You have hit one of the best (and most useful) points while coding in Kotlin.

    here which null safety operator I should use?

    It depends on the behavior you want to achieve. In Kotlin, you have to be very specific about what you want to do with null values, because the language is designed to be null-safe out of the box. Of course, targeting the JVM brings many challenges to a programming language. The eventuality of having null values is one of these, and Kotlin, as we'll see, handles this in a really smart manner.

    Purpose

    We could explain the full theory behind those two operators, but I believe an example is really all you need.
    Suppose you have a class, called Location, which we will declare in a nullable variable. In Kotlin, this is represented as val location: Location? Let's also say Location class has a property called lat, which is a nullable String, and a lon non-nullable String.

    data class User(val lat: String?, val lon: String)
    

    Operator ?.

    Kotlin Safe Call Operator Docs

    This operator is the safe call operator.
    If you use it in a call chain, it is checking that your code chain goes onto the next element just if the previous element is not null. Otherwise, null is retuned from the statement.

    val location: Location? = getLocation()
    println(location.lat)      // Compile-time error.
    println(location?.lat)    // Works fine.
    

    This happens because in the first case, the object before ?. is nullable, thus the Kotlin Compiler infers that accessing a nullable property can lead to NPEs.

    location could be null or not-null.
    We just don't know what it will be, and the Kotlin environment strictly makes sure that you are handling the eventuality of that value being null, as the type of our references variable is defined as nullable.

    However, a certain variable being null is something you developer may not know. Sometimes it is not even up to you to receive a null or non-null value.

    In this case you can safely stick with ?, knowing that this operator is your friend if you are unsure about whether what you're referencing will be null.

    val location: Location = getSafeLocation()
    val nullableLocation: Location? = getLocation()
    
    // Fine, may print "null" or the value, if present. 
    // println accepts nullable values
    println(location.lar) 
    
    // 100% sure it'll print the corrisponding String value
    println(location.lon)
    
    // May print "null", "null", or the lat String value.
    // The first "null" is because the ? operator will check if
    // nullableLocation is null. If it is, it stops the execution
    // chain and returns null. Otherwise, it assumes nullableLocation is safe
    // and goes on.
    //
    // The second "null" is because the value itself of lat
    // is declared as String? and Kotlin knows it may be null.
    // If println() did not accept null values, this call would fail,
    // but instead it prints "null" in case lat is indeed null.
    println(nullableLocation?.lat)
    
    // Since lat was the last element of the chain, it is not
    // delivered as the parameter type anymore, and thus if we
    // want to read a property from it we have to ensure that it isn't null.
    println(nullableLocation?.lat?.length)
    
    // This, as you may guess, returns wither null in case nullableLocation is null,
    // otherwise 100% sure it will print lon value, since it is not a String? but a String.
    println(nullableLocation?.lon)
    

    Operator !!.

    Kotlin Double-Bang Operator Docs

    This is the dreaded double-bang operator. Talking about syntax, it is very similar to ?., since it is used in the same place.

    To describe it in a really simple way: if anything before the call is null, your code will crash. Instantly. Without warnings. With !!. you're explicitly saying that

    Kotlin has to ignore any type nullability marker and to perform the operation you intend, even though it enters in a kind of danger zone. This is known as a force, because you're forcing the Kotlin environment to believe that the previous statement is not null. This operator best use case is when porting another library to Kotlin, or while handling API RESTful responses, situations where null values may come in, but because of environment/platform reasons, you know that some value can not be null. This helps you bringing type safety in the Kotlin world in the first place, if used properly.

    But for mainstream software, this feature is meant for a really specific and narrow usage: If you are 100% sure that the previous call is not null, go ahead.

    val location: Location? = getLocation()
    println(location!!.lon)
    

    The previous code may crash if location is

    Which one to use

    Both operators are type-transforming. They both turn nullable values into non-nullable ones. The way the do it is the changing factor.

    As a general rule, if you're sure the value you are targeting is not null, use !!., otherwise stick with ?.