RizTech Academy logo
RizTech Academy
Null SafetyLesson 4 of 415 min

Why !! is almost always the wrong answer

There is one operator in Kotlin that switches the null safety off. It is !!, the not-null assertion, and this short lesson is mostly a warning: it exists, you will see it, and reaching for it is almost always a sign you have given up on the very feature you came to Kotlin for.

What !! does

!! takes a nullable value and asserts, on your authority, that it is not null — converting String? to String:

val name: String? = "Kavita"
val length = name!!.length     // 6 — you promised name is not null

If your promise holds, it works. If it does not, !! throws a NullPointerException immediately, at that line:

val name: String? = null
val length = name!!.length     // throws NullPointerException
Exception in thread "main" java.lang.NullPointerException

Read that carefully: !! is you deliberately reintroducing the exact crash the whole module was about. You are telling the compiler "stop protecting me here, I know better" — and if you are wrong, you get the billion-dollar mistake back, in your own code, on purpose.

Why it is almost always wrong

Every time you write !!, you are choosing a crash over handling the null. And you almost always have a better option:

// instead of this:
val city = customer!!.address!!.city!!

// one of these is almost always better:
val city = customer?.address?.city ?: "Unknown"     // a sensible default
customer?.address?.city?.let { showCity(it) }        // do something only if present
val city = customer?.address?.city ?: return         // bail out cleanly

The safe call, Elvis, and let from the last two lessons handle the null without crashing. !! handles it by crashing. In almost every situation, one of the graceful options is what you actually want — a default, an early return, a skipped action — because "the value was missing" is a condition to handle, not a catastrophe to blow up on.

A trail of !! through a codebase — customer!!.address!!.city!! — is a well-known code smell. It usually means the author fought the type system until it gave up, rather than modelling the data honestly. Reviewers see !! and wince, and interviewers notice it.

The rare legitimate uses

"Almost always wrong" is not "always wrong". There are a few honest uses:

You have just checked, but the compiler cannot see it. Occasionally the compiler cannot prove a smart cast (a mutable property, cross-thread) even though you have verified non-null. Even then, copying to a local val and using the smart cast is usually cleaner than !!.

A value is genuinely guaranteed by the framework, not the type. Some Java or Android APIs return a nullable type that you know is non-null in your context — a view that must exist after setup, for instance. Here !! documents "I am certain", though a well-placed ?: with a clear error message is often better because it says why the assumption is safe.

A test, asserting a precondition. In test code, !! on something that must be present is a concise way to fail loudly if the setup is wrong.

The common thread: !! is acceptable only when null truly cannot happen and the compiler merely cannot prove it — never as a shortcut to skip handling a null that genuinely might occur.

If you must, at least explain why

When !! is genuinely justified, make the assumption visible — either with a comment stating why null is impossible, or by preferring an Elvis-with-error that carries the reason:

// !! with the reason it is safe
val token = savedToken!!   // set in onCreate before this can run; never null here

// often better — fails with an explanation instead of a bare NPE
val token = savedToken
    ?: error("savedToken must be set in onCreate before use")

The second form crashes just as !! would when the assumption is wrong — but with a message that tells the next developer (or you, at 2am) what invariant was violated, instead of a bare NullPointerException with no clue.

The rule to carry forward

Treat !! as a warning sign in your own code. When you type it, stop and ask: can this genuinely be null? If yes, handle it with ?., ?: or let. If no — if it truly cannot be null — ask why the type says it can, and whether the honest fix is to change the type or check at the boundary. !! should be rare, deliberate, and explained. A Kotlin developer is partly defined by how seldom they reach for it.

Check your work

What !! does. Asserts a nullable value is non-null, converting T? to T.

What happens if the value is actually null. It throws a NullPointerException at that line.

Why !! is almost always wrong. It reintroduces the crash null safety prevents, when a graceful option (default, early return, skipped action) is almost always available.

The three graceful alternatives. ?. (safe call), ?: (Elvis default/return/throw), ?.let (act only if present).

Why a trail of !! is a code smell. It signals the author fought the type system instead of modelling the data honestly.

The rare legitimate uses. A verified non-null the compiler cannot smart-cast; a framework guarantee the type does not express; a test precondition.

The common thread of legitimate use. Null genuinely cannot happen; the compiler just cannot prove it.

A better alternative when you need to fail. ?: error("why this cannot be null") — crashes with an explanation instead of a bare NPE.

The rule. !! should be rare, deliberate, and explained; ask "can this really be null?" every time you type it.

Practice

  1. Write val n: String? = "hi"; n!!.length and confirm it works.
  2. Change the value to null and run it. Read the NullPointerException — this is the crash you came to Kotlin to avoid, reintroduced on purpose.
  3. Take customer!!.address!!.city!! and rewrite it three ways: with a default, with ?.let, and with early return.
  4. Replace a !! with ?: error("explanation") and trigger it. Compare the message with a bare NPE.
  5. Search any Kotlin code you can find (or the reference repo later) for !! and, for each, judge whether it is one of the rare legitimate uses or a smell.
  6. Write down the one-sentence question to ask yourself every time you are about to type !!.

Official documentation

Next module — Functions: Kotlin functions are more flexible than most languages, and this is where that starts.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship