Skip to Content
Course content

68: Common Kotlin Interview Questions on Null Safety

Click on the "Edit" button in the top corner of the screen to edit your slide content.

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]"
Rating
0 0

There are no comments for now.

to be the first to leave a comment.