The Not-Null Assertion (!!)
Kotlin's escape hatch for bypassing null checks, and why reaching for it usually means reaching for the wrong tool.
2 min de lecture
Kotlin gives you one more way to work with a nullable value: the not-null assertion operator, !!. It tells the compiler "trust me, this isn't null" — and if you're wrong, it throws a NullPointerException right there, on the spot.
val nickname: String? = getNickname()
val length = nickname!!.lengthIf getNickname() returns null, that second line crashes immediately with NullPointerException: nickname must not be null. !! converts a nullable type back into its non-null counterpart by force, discarding every safety guarantee you'd otherwise get from ?. or ?:.
Why it exists at all
It's tempting to see !! as a design flaw, but it fills a real gap: sometimes you have outside information the compiler can't see. A common case is Java interop — a Java method annotated as always returning a value shows up in Kotlin as a platform type, where Kotlin can't verify nullability at all:
// legacyJavaLibrary.getConfig() might be declared to never return null,
// but Kotlin has no way to check a Java method's contract
val config = legacyJavaLibrary.getConfig()!!There are also cases in tests or prototypes where you'd rather fail loudly and immediately than write handling code for a case you've already ruled out.
Why to avoid it in real code
The problem with !! is that it reintroduces exactly the crash Kotlin's null safety was built to prevent — just with an exclamation point instead of silence. Every !! in a codebase is a spot where, if the assumption ever turns out wrong (an API changes, an edge case you didn't consider), the app crashes at runtime with no compiler warning beforehand.
Compare the two ways of handling the same nullable value:
// Risky: crashes if user is null, with no fallback behavior
fun printCity(user: User?) {
println(user!!.address!!.city)
}
// Safe: never crashes, and the caller decides what "missing" means
fun printCity(user: User?) {
println(user?.address?.city ?: "Unknown")
}The second version handles exactly the same possibility of null, but does it in a way that's visible in the type system and impossible to forget — there's no scenario where it throws.
A reasonable rule of thumb
Reach for !! only when you can state, in one sentence, a reason the value truly cannot be null at that point (for example, right after you just checked if (x == null) return, and even then a smart cast usually makes !! unnecessary). If you can't state that reason, that's a sign you should use ?., ?:, or a proper null check instead, and let the type system keep protecting you.
With null safety covered, the next section turns to control flow — starting with a very Kotlin idea: if isn't just a statement, it's an expression that produces a value.