Kotlin
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Null Safety
-
Section 4: Object-Oriented Kotlin
-
Section 5: Functional Kotlin
-
Section 6: Coroutines
-
Section 7: Collections Deep Dive
-
Section 8: Type System Deep Dive
-
Section 9: Interop and Tooling
-
Section 10: Kotlin DSLs and Patterns
-
Section 11: Testing and Quality
-
Section 12: Server-Side Kotlin
-
Section 13: Practical Projects
-
Section 14: Interview Practice
-
Section 15: More Practice Exercises
-
Section 16: More Standard Library
-
Section 17: Multiplatform Kotlin
-
Section 18: kotlin.collections In Depth
-
Section 19: kotlin.text In Depth
-
Section 20: kotlin.ranges and kotlin.sequences
-
Section 21: kotlin.io and File Handling
-
Section 22: kotlinx.coroutines Deep Dive
-
Section 23: kotlin.reflect
-
Section 24: Android Development with Kotlin Overview
-
Section 25: Kotlin Multiplatform Deep Dive
-
Section 26: Kotlin for Backend Deep Dive
-
Section 27: Kotlin Design Patterns
-
Section 28: Advanced Language Features
-
Section 29: More Practice Exercises
-
Section 30: More Interview Practice
-
Section 31: Kotlin Type System Deep Dive
-
Section 32: Kotlin Null Safety Advanced
-
Section 33: Kotlin Testing Deep Dive
-
Section 34: Kotlin Build Tooling Deep Dive
-
Section 35: Kotlin Serialization
-
Section 36: Kotlin Performance Considerations
-
Section 37: Kotlin Native Overview
-
Section 38: Kotlin for Data and Scripting
-
Section 39: More Coroutines Practice
-
Section 40: More Android-Adjacent Patterns
-
Section 41: More Practical Projects
-
Section 42: More Design and Architecture Practice
-
Section 43: Kotlin Language Evolution
-
Section 44: More Interview and Review
-
Section 45: Kotlin Delegation Patterns Deep Dive
-
Section 46: Kotlin Annotations Deep Dive
-
Section 47: Kotlin for Gradle Plugin Development
-
Section 48: Kotlin Concurrency Beyond Coroutines
-
Section 49: Kotlin Compiler Internals
-
Section 50: Real-World Kotlin Case Studies
-
Section 51: Final Practice Projects
-
Section 52: Kotlin for Server-Side Reactive Programming
-
Section 53: More Practice and Drills
-
Section 54: Kotlin Security Practices
13: Safe Calls and the Elvis Operator
You've already learned that Kotlin is obsessed with null safety. It's a good thing—I've spent far too many late nights in my career debugging NullPointerException crashes in Java that could have been avoided if the compiler had just yelled at me sooner. But once you start dealing with real-world data, you'll find that "nullable" isn't just a theoretical concept; it's everywhere.
Take a look at this snippet. Imagine we're building a user profile page, and we're trying to get the city where a user lives. Not every user provides an address, and even if they do, they might have left the city field blank.
data class Address(val city: String?)
data class User(val name: String, val address: Address?)
fun printUserCity(user: User) {
// This won't even compile!
println("User lives in: ${user.address.city}")
}
Fighting the Compiler over Nested Nulls
If you try to run that code, Kotlin will stop you dead in your tracks. It'll tell you that user.address could be null, and you can't access .city on a null object. Now, the "lazy" way to fix this—the way I see juniors do when they're frustrated—is to use the !! (not-null assertion) operator.
println("User lives in: ${user.address!!.city}")
Please, don't do this. Using !! is basically you telling the compiler, "Trust me, I know what I'm doing," right before the app crashes in production because a user decided not to enter their address. It turns a compile-time safety feature into a runtime disaster.
Chaining with the Safe Call Operator
The professional way to handle this is with the Safe Call operator: ?.. Think of this as a conditional check that happens automatically. If the object on the left is null, the whole expression just evaluates to null and stops right there, rather than throwing an exception.
fun printUserCity(user: User) {
val city = user.address?.city
println("User lives in: $city")
}
In this version, if address is null, city becomes null. If address exists but city is null, city still becomes null. It's a clean way to "drill down" into an object hierarchy without writing five nested if (x != null) blocks. I personally love how this reads; it's almost like a path of possibility.
Providing Defaults with the Elvis Operator
The problem now is that city is a String? (nullable). Often, your UI can't just display the word "null" to a customer. You want a fallback value—something like "Unknown" or "Not Provided".
This is where the Elvis operator ?: comes in. It's called the Elvis operator because if you tilt your head, the ?: looks like a pompadour hairstyle. It essentially says: "Give me the value on the left, but if that's null, give me the value on the right instead."
fun printUserCity(user: User) {
val city = user.address?.city ?: "Unknown City"
println("User lives in: $city")
}
Now we have a guarantee. Because we provided a fallback string, the resulting city variable is no longer a String?—it's a regular, non-nullable String. You've safely navigated a nullable object, a nullable property, and provided a default value, all in one readable line.
📋 Practical Task
Exercise: The Weather Station Data Cleaner
You are writing a module for a weather app. The data coming from the API is messy; sometimes the temperature is missing, and sometimes the wind speed is missing. You need to clean this data before it hits the UI.
Your Goal: Implement the getWeatherReport function. It should return a formatted string. If the temperature is null, use "N/A". If the wind speed is null, use "Calm".
data class WeatherData(
val temperature: Double?,
val windSpeed: Double?
)
fun getWeatherReport(data: WeatherData): String {
// YOUR CODE HERE
// Expected output format: "Temp: [value], Wind: [value]"
}
// Test cases to verify your logic:
fun main() {
val complete = WeatherData(22.5, 12.0)
val partial = WeatherData(22.5, null)
val empty = WeatherData(null, null)
println(getWeatherReport(complete)) // Should print: Temp: 22.5, Wind: 12.0
println(getWeatherReport(partial)) // Should print: Temp: 22.5, Wind: Calm
println(getWeatherReport(empty)) // Should print: Temp: N/A, Wind: Calm
}There are no comments for now.