-
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
68: Common Kotlin Interview Questions on Null Safety
Imagine you're a safety inspector at a construction site. You have a checklist of equipment that should be there. If you see a crane, you can operate it. But if there's just an empty plot of land where the crane should be, and you try to "operate" that empty space, the whole project grinds to a halt—or worse, someone gets hurt. In programming, that "empty space" is null, and the "hurt" is the dreaded NullPointerException (NPE).
When an interviewer asks you about null safety, they aren't just checking if you know where to put a question mark. They want to see if you understand how Kotlin shifts the burden of null-checking from runtime (where the app crashes for the user) to compile-time (where the IDE tells you to fix it before the app even runs). Here is how that maps to the code we write:
- The Missing Crane (Nullable Types): Using
String?is like marking a spot on your map as "Crane might be here." You've explicitly warned everyone that this value could be missing. - The Safety Check (Safe Call): Using
?.is like looking at the spot and saying, "If the crane is actually here, then lift the beam. Otherwise, just stand still." - The Backup Plan (Elvis Operator): Using
?:is like saying, "If the crane isn't here, use the manual hoist instead." - The Reckless Gamble (Not-null Assertion): Using
!!is like closing your eyes and screaming, "I don't care if it's there, JUST LIFT THE BEAM!" If it's not there, the whole site collapses.
Handling the "Billion Dollar Mistake" in Interviews
You'll often hear the phrase "the billion dollar mistake." That's a reference to Tony Hoare, the inventor of the null reference. Interviewers love to bring this up to see if you understand the philosophy of Kotlin. The core answer is that Kotlin distinguishes between types that can hold null and those that cannot. In Java, every single object could be null. In Kotlin, String and String? are effectively two different types.
val name: String = "Alex" // This can NEVER be null
val nickname: String? = null // This is explicitly allowed to be null
If you try to call name.length, Kotlin knows it's safe. If you try to call nickname.length, the compiler stops you. This is the "secret sauce" that makes Kotlin apps more stable than their Java predecessors.
The Battle Between Safe Calls and the Elvis Operator
A common interview curveball is asking when you should use ?. versus ?:. The safe call ?. is for chaining. If any link in the chain is null, the whole expression becomes null. The Elvis operator ?: is for providing a fallback.
Look at this specific scenario: getting a user's zip code from a deeply nested profile object.
val zipCode = user?.profile?.address?.zipCode ?: "00000"
I use this pattern constantly. If the user is null, or the profile is null, or the address is null, the safe calls just stop and return null. Then, the Elvis operator catches that null and says, "Fine, just use '00000' as the default." It keeps your code clean and prevents those massive if (user != null) { ... } nesting blocks that make code look like a sideways pyramid.
The "!!) Danger Zone
If you use !! in a technical interview, be prepared to defend it. Most seniors view the double-bang as a "code smell." It tells the compiler, "Trust me, I know more than you do." The problem is, we're often wrong.
However, there is one legitimate use case: when a value is null during initialization but is guaranteed to be non-null by the time a specific method is called, and you've already performed a check that the compiler isn't smart enough to track. But honestly? If you can use ?.let { } or ?: throw CustomException() instead, do it. It's more professional and far less likely to cause a 3 AM production alert.
lateinit vs. lazy: The Classic Trap
This is the most common "gotcha" question. Both delay the creation of an object, but they are fundamentally different. lateinit var is a promise that you will initialize the variable before using it. It only works with var and non-nullable types. by lazy is a delegate that computes the value the first time it's accessed and caches it. It only works with val.
I usually explain it like this: lateinit is for things injected by a framework (like Dagger or Android's onCreate), while lazy is for expensive objects that you might not even need to create unless the user hits a specific button.
📋 Practical Task
Refactoring the Fragile UserSession Manager
You've inherited a piece of legacy code for a UserSessionManager. The previous developer used !! everywhere, and as a result, the app crashes whenever a user logs out or has an incomplete profile. Your task is to refactor the getDisplayGreeting method to be completely null-safe.
Requirements:
- Remove all instances of
!!. - Use a safe call chain to access the user's first name.
- Use the Elvis operator to provide a default greeting of "Guest" if the name is missing.
- Ensure the final result is a non-nullable
String.
class User {
var profile: Profile? = null
}
class Profile {
var firstName: String? = null
}
class UserSessionManager {
var currentUser: User? = null
// TODO: Refactor this method to remove !! and use safe calls/Elvis operator
fun getDisplayGreeting(): String {
return "Hello, " + currentUser!!.profile!!.firstName!!
}
}
// Test cases to consider:
// 1. currentUser is null -> should return "Hello, Guest"
// 2. currentUser.profile is null -> should return "Hello, Guest"
// 3. currentUser.profile.firstName is null -> should return "Hello, Guest"
// 4. Everything is present -> should return "Hello, [Name]"
There are no comments for now.