Null safety looks like a small language feature, but understanding it can completely change how you handle missing data, avoid bugs, and write more reliable applications.
There's a particular kind of programming bug that's incredibly frustrating.
Your application works perfectly during testing. The buttons respond, the screens load, and everything seems fine.
Then someone uses it with slightly different data.
Maybe a user hasn't uploaded a profile picture. Maybe an API returns an empty field. Or perhaps a database doesn't contain the information your code expects.
Suddenly, something crashes.
The problem isn't necessarily complicated. Sometimes, your program simply assumed a value would always exist.
This is one of the reasons I find Kotlin interesting.
Instead of leaving developers to remember every possible null check, Kotlin makes missing values part of the language's type system.
It sounds like a small improvement.
But once you understand why it works, you start thinking differently about how software should behave.
The Problem Isn't Null. It's Our Assumptions.
Imagine you're building an application that displays a user's profile.
Every user must have a name, but adding a nickname is optional.
In many programming languages, it's easy to write code assuming both values will always exist.
Something like:
val nickname: String? = null
println(nickname.length)
If you're new to Kotlin, you might expect this to run and fail when it reaches the second line.
It doesn't.
Kotlin refuses to compile it.
The question mark in String? means the variable is allowed to contain either a string or null.
Because Kotlin knows the value might be missing, it won't let you access .length directly.
Compare that with:
val name: String = "Alex"
println(name.length)
This works because name is declared as a non-nullable string.
Kotlin's compiler understands the difference.
That's the first important lesson: your code should express what data is allowed to be missing, rather than hoping every value will be available.
A Question Mark Can Replace a Lot of Defensive Code
Let's return to our optional nickname.
Suppose you want to display its length when it exists.
You could write:
val nickname: String? = "Alex"
if (nickname != null) {
println(nickname.length)
}
There's nothing wrong with that approach.
In fact, explicit conditions are often easier to understand when you're learning.
But Kotlin provides a shorter alternative:
println(nickname?.length)
This is called a safe call.
If nickname contains a string, Kotlin returns its length.
If it's null, the expression returns null instead of attempting to access a property on a missing object.
Now imagine you're working with information returned by an API.
A user might have an address, and that address might have a city.
You could access it like this:
val city = user.address?.city
If address is nullable and happens to be missing, the expression safely returns null.
Of course, this doesn't solve every problem with incomplete data.
But it removes a category of assumptions that developers would otherwise have to manage manually.
Sometimes You Need a Fallback, Not Another Null
Here's where Kotlin becomes especially practical.
Returning null is safe, but it isn't always useful.
Suppose you're displaying a greeting.
If the user has a nickname, you want to use it. Otherwise, their regular name is perfectly fine.
Kotlin has an operator for exactly this situation.
val nickname: String? = null
val name = "Alex"
val displayName = nickname ?: name
println("Welcome, $displayName!")
Output:
Welcome, Alex!
The ?: symbol is called the Elvis operator.
It means: use the value on the left if it isn't null; otherwise, use the value on the right.
This is useful for things like optional descriptions, configuration settings, and default display values.
But there's something worth understanding before using it everywhere.
Not every missing value should be replaced with a default.
If an API doesn't return the user's account balance, replacing that value with zero could display incorrect financial information.
If a delivery address is missing, inventing a default address would be even worse.
Sometimes the correct response is to show an error or ask for the missing information.
Good programming isn't just about preventing crashes.
It's about deciding what the application should do when reality doesn't match your expectations.
Let the User Make Mistakes Without Breaking Everything
Consider another everyday scenario.
You're building a simple expense calculator.
The user enters a quantity, and your program needs to convert it into a number.
A beginner might write:
val quantity = input.toInt()
This works when the input is something like "5".
But what happens when someone enters "five"?
Kotlin throws a NumberFormatException.
Instead, you can use:
val quantity = input.toIntOrNull()
Now invalid input produces null.
You can handle that result deliberately:
fun calculateTotal(input: String): String {
val quantity = input.toIntOrNull()
?: return "Please enter a valid number"
if (quantity <= 0) {
return "Quantity must be greater than zero"
}
val price = 100
return "Total: ${quantity * price}"
}
Notice what's happening here.
The program isn't silently ignoring bad input.
It's explaining the problem to the user.
For a small example, that might not seem significant. In an actual application, however, handling unexpected values properly can make the difference between a confusing failure and a useful experience.
The Dangerous Shortcut Beginners Should Understand
While learning Kotlin, you'll probably encounter code like this:
val username: String? = null
println(username!!.length)
The double exclamation mark, !!, tells Kotlin to treat the value as non-null.
If the value actually is null, the code throws a NullPointerException.
In other words, you're overriding the compiler's protection.
There are situations where developers use !! because they have a strong guarantee that a value exists.
But it's worth being cautious with it.
If you find yourself adding !! simply to make compiler errors disappear, there's probably a better way to handle the data.
Try an explicit null check, a safe call, or a meaningful fallback instead.
The point of learning Kotlin isn't to find shortcuts around its safety features.
It's to understand why those protections exist.
Safer Code Starts With Better Data Models
One of the most useful habits you can develop is deciding which fields are genuinely optional before writing application logic.
Consider this simple Kotlin data class:
data class UserProfile(
val id: Int,
val name: String,
val nickname: String?,
val bio: String?
)
This model communicates several things immediately.
Every profile must have an ID and name.
A nickname and biography are optional.
Anyone reading the code can understand those rules without searching through dozens of functions.
And Kotlin will help enforce them.
This matters as projects grow.
A small application might contain only a few classes. A larger application can involve data coming from APIs, databases, user input, and external libraries.
When your types accurately describe that data, other developers have fewer assumptions to make.
There's one subtle point here: Kotlin's null safety isn't a replacement for validation.
An empty string isn't the same as null.
A negative quantity isn't necessarily valid just because it has the correct type.
And when Kotlin interacts with Java code, some null-related risks still require special attention.
The language helps you express your expectations, but you still have to decide whether incoming data makes sense.
How I'd Recommend Learning This Properly
Reading about a programming concept is useful.
Actually using it makes the difference.
If you're learning Kotlin, try building something small enough that you can deliberately experiment with missing values.
A simple user profile application works well.
Start by creating a UserProfile data class with a required name and optional nickname.
Next, write a function that displays the nickname when available and uses the name otherwise.
Then add a biography field and make your application display "No bio added yet" when appropriate.
Finally, test your code with different combinations of missing information.
You don't need Android Studio or an entire mobile application for this exercise. A simple Kotlin console program is enough.
For anyone starting from the basics, Codevra has a structured Kotlin course that introduces variables, types, null safety, functions, classes, collections, and other topics through examples and exercises.
It's useful to work through these concepts in order, but don't stop at reproducing the examples.
Change their inputs. Remove values. Try invalid data. Predict what will happen before running the program.
That's where the understanding starts to become practical.
Writing Less Code Isn't Always the Goal
Kotlin is often praised for its concise syntax.
And yes, writing fewer lines can be satisfying.
But fewer lines don't automatically mean better code.
This:
val result = value?.trim()?.takeIf {
it.isNotEmpty()
} ?: "Unknown"
might be perfectly reasonable once you're comfortable with Kotlin.
For someone encountering safe calls, lambdas, and the Elvis operator for the first time, however, a longer version with explicit conditions might be easier to follow.
Readability matters.
There's no prize for fitting an entire function onto one line.
Choose the version that communicates your intentions clearly, especially when you're working with other developers.
You can always simplify your code later when you understand what it's doing.
Final Thoughts
One of the biggest changes that comes with programming experience is learning to question your assumptions.
What happens if this field is missing?
What if the user enters the wrong value?
What if an external service returns something unexpected?
What should the application do next?
Kotlin's null safety encourages you to ask those questions before a problem reaches the user.
It won't eliminate every bug, and it certainly won't replace careful testing.
But it offers something valuable: a way to make important assumptions visible in your code.
And once you start thinking about missing data as something to design for, rather than something to fix after an application crashes, you begin writing better software.
Not necessarily more complicated software.
Just software that's more prepared for the real world.
`
Further Reading
If you want to explore Kotlin beyond the examples in this article, these resources are worth bookmarking:
Kotlin Tutorial for Beginners — A structured course covering Kotlin fundamentals, null safety, functions, classes, coroutines, and more, with examples, exercises, and quizzes.
Official Kotlin Documentation — Null Safety — A detailed explanation of nullable types, safe calls, the Elvis operator, and other language features.
Google Android Developers — Use Nullability in Kotlin — A hands-on introduction to working with nullable values.
Kotlin Basic Syntax Guide — A useful reference for learning and revisiting Kotlin fundamentals.
`
Top comments (0)