Code Smell 208 - Don't Use Null for Real Places
2026-8-3 03:11:23 Author: hackernoon.com(查看原文) 阅读量:8 收藏

You can avoid null if you try

TL;DR: Don't use null for real places

Problems 😔

  • Tight Coupling
  • Unexpected Results

Solutions 😃

  1. 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

  • Null

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

Without Proper Instructions 📵

With Specific Instructions 👩‍🏫

Conclusion 🏁

Don't use Null to represent real objects

Relations 👩‍❤️‍💋‍👨

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxvi

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxii

More Information 📕

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."

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


This article is part of the CodeSmell Series.


文章来源: https://hackernoon.com/code-smell-208-dont-use-null-for-real-places?source=rss
如有侵权请联系:admin#unsafe.sh