Nested null check when only first one is needed?

given code below, why kotlin enforce you to do nested null check , like bellow printCountry function it has 4 null checks with “?.” when just first check is really needed?

fun printCountry(person: Person?) {
println(person?.address?.city?.country?.name ?: “Unknown Country”)
}

data class Country(
val name: String,
val isoCode: String
)

data class City(
val name: String,
val state: String,
val country: Country
)

data class Address(
val street: String,
val number: String,
val postalCode: String,
val city: City
)

data class Person(
val name: String,
val age: Int,
val address: Address
)

I hope it is clear why this happens: On every level, the input may be null, and you have to deal with it somehow.

If you are annoyed by the question marks, you can always write something like person?.apply{address.city.country.name} ?: "Unknown Country" or longer, but easier to understand if (person != null) person.address.city.country.name else "Unknown Country"

If you really hate it, how about this:

fun <T, R> T?.whenNull(default: R, body: T.()-> R): R =
    if (this == null) default else body(this)

println(person.whenNull("Unknown Country") { address.city.country.name })
2 Likes

Perhaps we can make this clearer by break it down, step by step.

First, we have person, which is nullable.

Next we call ?.address on that. If person was null, then that evaluates to null immediately; otherwise, it gets the value of the address property, which is an Address object.

Then we call ?.city on the result. The result could be null (if person was null), in which case this evaluates to null too; otherwise, it gets the value of the city property.

Then we call ?.name on the result of that. The result could be null (if person was null), in which case this evaluates to null too; otherwise, it gets the value of the name property.

The final part is a straightforward elvis operator, which checks whether the result of all that is null, and if so substitutes a given string.

All well and good.

But what if you instead dropped the subsequent question marks, to give person?.address.city.country.name ?: “Unknown Country”?

The first parts would work as before: person?.address would evaluate to null if person was null, or to an Address object.

It would then try to access the result’s city field, without any further check. But what if the result was null (because person was null)? It wouldn’t have a city field — accessing a field of a null object is of course impossible! So the compiler doesn’t allow this. (And the same would then apply for the remaining . operators.)

At each stage, the preceding expression could have evaluated to null, and so we need to handle that case (with a ?. or ?: or whatever).