Pocket Studio Academy
HomePart 11.13

Null safety — the billion-dollar mistake

Full course11 min read·4 questions

Null is how code says "there is nothing here". Used carelessly it is the single most common crash in software history. Kotlin makes it a compile-time question instead of a 3 a.m. bug report — this lesson shows exactly how.

A mistake worth a billion dollars

In 1965 a computer scientist called Tony Hoare added a small feature to a language he was designing: a value meaning nothing here. He called it the null reference. It was easy to implement, so he put it in.

Forty-four years later he stood up at a conference and apologised for it. His phrase was the billion-dollar mistake — his estimate of the damage it had done in crashes, security holes and lost work since.

He was not exaggerating. Open any app store review page and read the one-star reviews. "Crashes when I tap save." "Closes as soon as I open it." A very large share of those are one specific bug, and it has a name.

This lesson is about that bug, and about why the language you are learning has largely killed it.

Think of it like this

Picture the locker room at a swimming pool.

Most lockers have a coat in them. Some are empty. Now imagine walking up to locker 12 and asking: what colour is the coat in there?

If there is a coat, fine. If the locker is empty, the question has no answer. You cannot invent a colour for a coat that does not exist. All you can do is stop and say "there is no coat".

In most programming languages you only find out the locker was empty at the moment you reach in — which, in an app, means while a real person is using it. The app stops.

does something better. It puts a label on every locker saying whether it is allowed to be empty, and it refuses to build your app until you have said what should happen when it is.

Why null exists at all

It would be tempting to say "just never have nothing". But nothing is a real, honest state of the world:

  • A player has not chosen a nickname yet.
  • You searched the list and there was no match.
  • You looked up a that is not in the — exactly what happened in Lesson 1.11.
  • A note has not been saved yet, so it has no database id.

The tempting alternatives are worse. Using 0 for "no score" makes a genuine zero indistinguishable from a missing one. Using "" for "no nickname" means an empty nickname and no nickname look identical.

is the honest answer. The bug was never null itself. The bug is forgetting that something might be null — and then asking it a question anyway.

What a NullPointerException actually is

A name in your program does not hold a whole object. It holds a pointer — an address where the object lives. null is the address of nowhere.

When your code says name.length, the phone follows that address to ask the object how long it is. If the address is nowhere, there is nothing there to ask. The processor cannot invent an answer, so it throws an . Unhandled, that closes the app.

Here is the real thing, straight out of :

text
1java.lang.NullPointerException: Attempt to invoke
2virtual method 'int java.lang.String.length()'
3on a null object reference

Read it slowly and it is almost friendly. Something tried to call length() on a String, and that String was null. The underneath tells you which line of which file. Usually shortened to NPE, and it is the crash you will meet more than any other in other people's code.

Kotlin's fix: one type becomes two

In Kotlin, every you have met so far comes in two versions.

Playground.ktkotlin
1var name: String = "Ada"
2var nick: String? = "Ada"

String can hold text. String? can hold text or null. That single question mark is the whole idea, and it changes what the will let you write.

Playground.ktkotlin
1name = null   // does not compile
2nick = null   // perfectly fine

The first line fails with:

text
e: Null can not be a value of a non-null type String

And this is the part that matters: you now find out in the editor, before the app is built, instead of on a stranger's phone six weeks later. That is what means. Kotlin has not made null disappear. It has moved the discovery of it forward in time by about six weeks.

A type with a question mark is called a . One without is non-nullable, and the compiler guarantees it can never be null. No check needed, ever.

The four tools

1. ? on the type — "this one may be empty"

You have just seen it. Only add the question mark when a value genuinely can be missing. A nullable type is a promise to every future reader that they must handle the empty case, so do not hand it out for free.

2. ?. — the safe call

You cannot write nick.length, because nick might be nothing. Try it and you get the error you will see most often in this lesson:

text
1e: Only safe (?.) or non-null asserted (!!.) calls are
2allowed on a nullable receiver of type String?

The is a dot with a question mark in front:

Playground.ktkotlin
println(nick?.length)

It means: if the thing on the left is null, stop and produce null; otherwise carry on. So with nick = "Ada" this prints 3, and with nick = null it prints null — no crash either way.

The catch is that the answer is now Int? rather than Int. Safe calls pass the nullability along the chain, which is honest but does need dealing with. Which brings us to the best tool of the four.

3. ?: — the Elvis operator

Two characters that mean "or else this instead":

Playground.ktkotlin
1val letters: Int = nick?.length ?: 0
2println("Hi, ${nick ?: "stranger"}")

If the left-hand side is null, the right-hand side is used. Note the type of letters: plain Int, no question mark. The is where nullability ends — after it, you are back on solid ground.

It is named after Elvis Presley. Turn ?: sideways and you get the eyes and the quiff.

?: is not limited to values, either. This is a very common shape in real code:

Playground.ktkotlin
1fun show(nick: String?) {
2  val real = nick ?: return
3  println(real.uppercase())
4}

Get me the nickname, or give up and leave the function now. After that line, real is a plain String for the rest of the function.

4. !! — the promise you should almost never make

Playground.ktkotlin
val letters = nick!!.length

Two exclamation marks. It means: compiler, I am certain this is not null; stop asking.

And if you are wrong, the app closes.

Read this twice

does not remove the null. It removes the check. What you get back is the exact 1965 behaviour Tony Hoare apologised for: a crash, at runtime, in front of a user.

Every !! you write is a sentence that reads: "I am so sure this cannot be null that I am willing to close the app in someone's face if I am wrong."

Sometimes that sentence is true. Almost always, ?: with a sensible fallback is truer, and takes the same number of keystrokes. If you find yourself typing !! to make an error go away, the error was right and you were wrong.

And one bonus: the compiler is paying attention

You do not always need an operator. Plain old works, because of a feature called a :

Playground.ktkotlin
1if (nick != null) {
2  println(nick.length)
3}

Inside those braces, nick is treated as a plain String — no ?. needed. The compiler followed your check and drew the obvious conclusion. This is often the most readable option when you have several things to do with the value.

Try it in Pocket Studio
  1. Open Pocket Studio, tap Projects, and open your Kotlin practice project.
  2. Tap Editor and open Playground.kt.
  3. Select everything in the file and delete it.
  4. Type the thirteen-line program from the walkthrough above.
  5. Tap Run and read the Output panel.

You should see exactly this:

text
1Hi, Ada
2Letters: 3
3Loud: ADA
4Hi, stranger
5Letters: 0
  1. Now break it on purpose. Change line 4 to println("Letters: ${name.length}") — drop the ?. and the ?: 0.
  2. Tap Run. It does not run. Read the error: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?. That is Kotlin catching the billion-dollar mistake before it ever reaches a phone.
  3. Now make it worse: change that line to println("Letters: ${name!!.length}").
  4. Tap Run. It compiles, prints the first three lines, and then dies on greet(null) with a NullPointerException. You told the compiler to trust you, and you were wrong.
  5. Put line 4 back the way it was.
Error Doctor5 common errors
e: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
MeansYou used a plain dot on something that might be null. The receiver is the thing to the left of the dot.
FixUse ?. and then decide what happens when it is null — usually with ?:. For example name?.length ?: 0. Do not reach for !! just to silence this.
e: Null can not be a value of a non-null type String
MeansYou tried to store null in something declared as plain String, which by definition can never hold null.
FixIf it really can be missing, declare it as String?. If it cannot, find out why null got in there — that is usually the actual bug.
e: Type mismatch: inferred type is String? but String was expected
MeansYou passed a nullable value to something that only accepts a non-nullable one — a function parameter, or a variable declared without a question mark.
FixGive it a value it can rely on: takeName(nick ?: "anon"). Or change the receiving side to accept String? if missing is a legitimate case there.
Exception in thread "main" java.lang.NullPointerException
MeansThe app compiled and then crashed at runtime. Almost always a !! on something that turned out to be null after all.
FixFind the !! on the line named in the stack trace and replace it with ?: and a real fallback, or an if (x != null) block.
w: Unnecessary safe call on a non-null receiver of type String
MeansA warning, not an error — the code still builds. You wrote ?. on something that can never be null, so the check does nothing.
FixChange ?. back to a plain .. It is worth doing: leaving it in suggests to the next reader that this value can be null, which is misleading.
Recap
  • means there is genuinely nothing here, and that is often the honest answer. The bug is forgetting it might happen.
  • A is what happens when running code asks something of a value that is not there.
  • Kotlin splits every type in two: String can never be null, String? might be. The enforces it, so you find out in the editor rather than in a review.
  • carries on only if there is something there. supplies a fallback and ends the nullability. if (x != null) gives you a .
  • is a promise that the app should close if you are wrong. Make it about twice a year.
  • Next: classes — how to bundle a name, a score and the things a player can do into one thing you can pass around.