You can avoid null if you try
TL;DR: Don't use null for real places
Problems π
Tight Coupling
Unexpected Results
Solutions π
- Model unknown locations polymorphically
Context π¬
Null Island is a fictional place that sits at 0Β°N 0Β°E, at the intersection of the Prime Meridian and the Equator in the Atlantic Ocean.
Many GPS systems place data with missing or invalid coordinates at this exact point. That's where the name "Null Island" comes from.
There's no landmass at this location. It's open ocean.
This point has become a popular reference for geographic information systems (GIS) and mapping software, because it helps filter out errors in location data.
Data visualization specialists started using the term around 2008, after noticing that failed geocoding requests and invalid coordinate entries often defaulted to (0, 0).
Natural Earth, a public domain mapping dataset, deliberately includes a fictional one-square-meter island at that exact point to help catch geocoding errors. The trick works: researchers have found more than 300,000 Flickr photos and countless social media posts geotagged to Null Island, and during the COVID-19 pandemic, Johns Hopkins' tracking dashboard plotted confirmed cases there whenever the real location was missing.
Sample Code π»
Wrong π«
class Person(val name: String,
val latitude: Double,
val longitude: Double)
fun main() {
val people = listOf(
Person("Alice", 40.7128, -74.0060),
// New York City
Person("Bob", 51.5074, -0.1278),
// London
Person("Charlie", 48.8566, 2.3522),
// Paris
Person("Tony Hoare", 0.0, 0.0)
// Null Island
)
for (person in people) {
if (person.latitude == 0.0 && person.longitude == 0.0) {
println("${person.name} lives on Null Island!")
} else {
println("${person.name} lives at " +
"(${person.latitude}, ${person.longitude}).")
}
}
}
Right π
abstract class Location {
abstract fun calculateDistance(other: Location): Double
abstract fun ifKnownOrElse(knownAction: (Location) -> Unit,
unknownAction: () -> Unit)
}
class EarthLocation(val latitude: Double, val longitude: Double):
Location() {
override fun calculateDistance(other: Location): Double {
val earthRadius = 6371.0
val latDistance = Math.toRadians(
latitude - (other as EarthLocation).latitude)
val lngDistance = Math.toRadians(
longitude - other.longitude)
val a = sin(latDistance / 2) * sin(latDistance / 2) +
cos(Math.toRadians(latitude)) *
cos(Math.toRadians(other.latitude)) *
sin(lngDistance / 2) * sin(lngDistance / 2)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
return earthRadius * c
}
override fun ifKnownOrElse(knownAction:
(Location) -> Unit, unknownAction: () -> Unit) {
knownAction(this)
}
}
class UnknownLocation : Location() {
override fun calculateDistance(other: Location): Double {
throw IllegalArgumentException(
"Can't calculate distance" +
" from an unknown location.")
}
override fun ifKnownOrElse(knownAction:
(Location) -> Unit, unknownAction: () -> Unit) {
unknownAction()
}
}
class Person(val name: String, val location: Location)
fun main() {
val people = listOf(
Person("Alice", EarthLocation(40.7128, -74.0060)),
// New York City
Person("Bob", EarthLocation(51.5074, -0.1278)),
// London
Person("Charlie", EarthLocation(48.8566, 2.3522)),
// Paris
Person("Tony", UnknownLocation())
// Unknown location
)
val rio = EarthLocation(-22.9068, -43.1729)
// Rio de Janeiro coordinates
for (person in people) {
person.location.ifKnownOrElse(
{ location -> println("${person.name} is " +
"${location.calculateDistance(rio)} kilometers " +
"from Rio.") },
{ println("${person.name} is at an unknown " +
"location.") }
)
}
}
Detection π
[X] Semi-Automatic
You can check for special numbers used as nulls
Level π
[X] Intermediate
Why the Bijection Is Important πΊοΈ
Real coordinates map to real places on Earth. That is the bijection between your model and the MAPPER.
When you reuse (0, 0) to mean "unknown location," you collapse two different concepts into a single representation.
A real point in the Atlantic Ocean and the absence of data aren't the same thing, and your model shouldn't pretend they are.
Modeling the unknown location as its own type keeps the mapping honest.
Real coordinates always mean a real place, and missing data gets its own explicit representation instead of borrowing one that already means something else.
AI Generation π€
AI generators create this smell often.
When you ask for a location class, they default to primitive latitude and longitude doubles and reach for (0.0, 0.0) as a convenient placeholder for missing data, the same shortcut developers take under deadline pressure.
AI Detection π§²
AI generators rarely catch this smell on their own.
Unless you explicitly ask for a type that represents unknown locations, they treat (0.0, 0.0) as a normal default value and won't suggest polymorphism unless you request it.
Try Them! π
Remember: AI Assistants make lots of mistakes
Suggested Prompt: Replace the (0.0, 0.0) sentinel value with a polymorphic Location type that separates known coordinates from an explicit unknown location
Conclusion π
Don't use Null to represent real objects
Relations π©β€οΈππ¨
https://coderlegion.com/9248/code-smell-12-null
https://maximilianocontieri.com/code-smell-126-fake-null-object
https://maximilianocontieri.com/code-smell-160-invalid-id-9999
https://coderlegion.com/6462/null-the-billion-dollar-mistake
https://en.wikipedia.org/wiki/Null_Island
A research buoy once sat right at 0Β°N 0Β°E collecting climate data until 2021, and the exact antipode of Null Island, at 0Β°N 180Β°E, is nicknamed "Antinull Island."
https://www.youtube.com/v/daiCb6pT1qY
Disclaimer π
Code Smells are my opinion.
The billion dollar mistake of having null in the language. And since JavaScript has both null and undefined, it's the two billion dollar mistake.
Anders Hejlsberg
https://coderlegion.com/13786/software-engineering-great-quotes
This article is part of the CodeSmell Series.
https://coderlegion.com/10942/how-to-find-the-stinky-parts-of-your-code