Start learning free

LESSON 81 OF 348 · CONDITIONAL STATEMENTS

Null Safety in Kotlin: Nullable Types, ?. and ?:

Lesson Read the full explanation

In Java, ANY variable can secretly be null — and your program only finds out when it crashes with the infamous NullPointerException. Kotlin was designed to kill that bug at compile time: a plain String can never hold null. When a value might be absent, you must say so in the type with a question mark — String? is a nullable type, and the compiler refuses to let you touch it until you have dealt with the null case.

var name: String = "Kotlin"
name = null                    // rejected at compile time!
var nickname: String? = null   // fine — the ? allows null

The assignment to name never even compiles:

error: null can not be a value of a non-null type String

And you cannot call members on a String? directly — nickname.length gives:

error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

Three ways past that error:

// 1. safe call ?. — the value, or null when absent (safe)
nickname?.length          // Int? : the length, or null

// 2. Elvis ?: — the value, or your fallback when null (safe)
nickname ?: "none"        // never null — always a real String

// 3. !! — "trust me, it's not null": the value, or CRASH (dangerous)
nickname!!.length         // throws if nickname is null

They chain nicely too: nickname?.length ?: 0 reads "the length if there is one, otherwise 0".


The trap:

!! silences the compiler — it does NOT make the value exist. This compiles without a single complaint:

val nickname: String? = null
println(nickname!!.length)   // "trust me"... but it IS null

...and dies the moment it runs:

Exception in thread "main" java.lang.NullPointerException
	at MainKt.main(Main.kt:2)

That is the exact crash Kotlin's type system exists to prevent — !! is you overriding the safety, which is why experienced Kotlin developers treat every !! as a code smell. The rule: reach for ?. and ?: by default, and use !! only when you can prove the value is there.


Assignment
Follow the Coding Tutorial and try all three on a present and an absent value:

  • declare val present: String? = "Kotlin" and val missing: String? = null,
  • print the length of each with the safe call ?.length — no crash, even for missing,
  • print each value with the Elvis fallback ?: "none",
  • print present!!.length — safe here, because present certainly holds a value.

The exact expected output is:

present length = 6
missing length = null
present or default = Kotlin
missing or default = none
forced length = 6

Hint
Build each line with a string template, e.g. println("present length = ${present?.length}"). Note that ${missing?.length} prints the word null — the safe call returned null, and the template prints it as text.


STEP 1 OF 7
    Ctrl + Enter to check

    KOTLIN · CONDITIONAL STATEMENTS

    Null Safety in Kotlin, explained

    The problem null safety solves

    In many languages, any variable that holds an object can secretly hold "nothing", and the program finds out only when it tries to use that nothing and crashes. Kotlin moves that check to compile time. A plain type such as String can never hold null. When a value might be absent, you say so in the type by adding a question mark: String? is a nullable type. The compiler then refuses to let you use the value until you have dealt with the null case.

    var name: String = "Kotlin"
    name = null                    // rejected at compile time
    var nickname: String? = null   // fine: the ? allows null
    

    The assignment to name fails to compile:

    error: null cannot be a value of a non-null type 'String'.
    

    The type of name says it always has a value, so assigning null breaks that promise. The ? is part of the type, so String and String? are two different types. A String can be used wherever a String? is expected, but not the other way round.

    Why you can't just use a nullable value

    With a nullable type, even a basic member access is rejected. For var nickname: String? = null, the expression nickname.length gives:

    error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type 'String?'.
    

    That error is the point of the feature. The compiler knows the value could be absent, so it makes you choose what happens in that case.

    Three ways to handle a nullable value

    1. The safe call ?.

    nickname?.length reads the length only when nickname holds a value. If it is null, the whole expression is null and nothing crashes. Its type is Int?, an Int that may be absent.

    2. The Elvis operator ?:

    nickname ?: "none" gives the left side when it has a value, and the right side when the left is null. Its type is String, never null, so you can use the result freely. It combines with the safe call: nickname?.length ?: 0 reads "the length if there is one, otherwise 0".

    3. The non-null assertion !!

    nickname!!.length tells the compiler "trust me, this is not null". If you are wrong, the program throws a NullPointerException at that point. It silences the compiler without making the value exist:

    val nickname: String? = null
    println(nickname!!.length)
    

    This compiles with no complaint and then stops when it runs:

    Exception in thread "main" java.lang.NullPointerException
    

    That is exactly the crash the type system exists to prevent. Reach for ?. and ?: by default and use !! only when you can prove the value is present.

    Checking for null yourself

    A plain null check also works. After if (nickname != null), Kotlin treats a val or local variable as non-null inside that branch, so nickname.length compiles there. This is called a smart cast. The same applies to the right side of nickname != null && nickname.length > 3.

    Common mistakes

    • Adding !! to make an error go away. The error disappears, but the risk moves to run time.
    • Expecting ?. to give a plain value. missing?.length has the type Int?. To use it as an Int, add a default with ?:.
    • Thinking val means non-null. val only stops reassignment. Whether the value may be null depends on the ? in the type.
    • Printing a null by surprise. In a string template, a null prints as the text null. That is not an error, but it may not be what you want to show.

    The lesson's exercise

    The task is to try all three tools on a value that is present and one that is absent, and to produce exactly this output:

    present length = 6
    missing length = null
    present or default = Kotlin
    missing or default = none
    forced length = 6
    
    1. Declare present with val, give it the type String? and the value "Kotlin". A nullable type can hold a normal value too.
    2. Declare missing as a String? set to null. Without the ?, the compiler rejects it.
    3. Print the two lengths with a safe call inside a string template, ${present?.length}. For present it gives 6. For missing the safe call returns null and the template prints that as the word null.
    4. Print the two fallbacks with ${present ?: "none"} and ${missing ?: "none"}. The first keeps Kotlin and the second uses none.
    5. Print the forced length with ${present!!.length}. It is safe here because present holds a value.
    val present: String? = "Kotlin"
    val missing: String? = null
    println("present length = ${present?.length}")
    println("missing length = ${missing?.length}")
    println("present or default = ${present ?: "none"}")
    println("missing or default = ${missing ?: "none"}")
    println("forced length = ${present!!.length}")
    

    The braces in ${...} let a string template hold a whole expression rather than just a variable name. Writing "$present?.length" would not work: the template takes only present, and ?.length stays as plain text.

    Key takeaways

    • String never holds null. String? may, and the compiler enforces the difference.
    • ?. returns the member or null, ?: supplies a default, and !! crashes on null.
    • Prefer ?. and ?:, and treat every !! as a risk you have to justify.
    Keep going with KotlinAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free