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
- Write
val n: String? = "hi"; n!!.lengthand confirm it works. - Change the value to null and run it. Read the
NullPointerException— this is the crash you came to Kotlin to avoid, reintroduced on purpose. - Take
customer!!.address!!.city!!and rewrite it three ways: with a default, with?.let, and with early return. - Replace a
!!with?: error("explanation")and trigger it. Compare the message with a bare NPE. - 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. - Write down the one-sentence question to ask yourself every time you are about to type
!!.
Official documentation
- Kotlin — Null safety: the !! operator — What the not-null assertion does, and its risk.
- Kotlin — Null safety — The graceful alternatives, all in one place.
- Kotlin standard library — error — The
error(...)used for a failing-with-a-reason alternative.
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