← Back to Kotlin Course | Chapter 5: Null Safety | Lesson 6 of 6

Null Safety Best Practices

Best practices for null safety are the smart habits that help you avoid empty-box surprises altogether, rather than just reacting to them.

Prefer Non-Null by Default

Design functions and classes so most properties and parameters are non-nullable, reserving ? only for cases where absence is truly meaningful, which keeps the rest of your code free of constant null checks.

Example: Prefer Non-Null by Default

markup
data class Product(val name: String, val price: Double, val discountCode: String? = null)

fun main() {
    val product = Product("Book", 15.0)
    println("${product.name}: $${product.price}, discount=${product.discountCode ?: "none"}")
}

Validate at the Boundary

When data comes from outside your program, such as parsed text or a network response, convert it into a proper non-null domain type as early as possible instead of passing raw nullable values deep into your logic.

Example: Validate at the Boundary

markup
fun parseAge(input: String?): Int {
    return input?.toIntOrNull() ?: error("Invalid or missing age: $input")
}

fun main() {
    println("Age: ${parseAge("42")}")
}

Avoid Nullable Collections

Returning an empty list instead of a nullable list means callers can safely iterate or check size without an extra null check, simplifying calling code considerably.

Note: An empty list is far easier to work with than null for representing 'no results'.

Example: Avoid Nullable Collections

markup
fun findMatches(query: String, items: List<String>): List<String> {
    return items.filter { it.contains(query) }
}

fun main() {
    val results = findMatches("kt", listOf("main.kt", "readme.md"))
    println("Found ${results.size} matches: $results")
}

Reserve !! for Truly Impossible Nulls

Treat !! as a signal that something is architecturally wrong if it ever actually throws; use it only in the rare case where the surrounding logic guarantees non-null, and prefer safer operators everywhere else.

Example: Reserve !! for Truly Impossible Nulls

markup
fun main() {
    val cache = HashMap<String, String>()
    cache["greeting"] = "Hello"
    if ("greeting" in cache) {
        println(cache["greeting"]!!)
    }
}
Common Mistakes
  1. Designing APIs that return nullable types when an empty collection or a sensible default would communicate the same information more safely.
  2. Sprinkling !! throughout a codebase as a quick fix instead of addressing why a value might unexpectedly be null in the first place.
  3. Not validating external data (network responses, user input) at the boundary of your program, letting nulls leak deep into business logic.
Chapter Summary
  • Prefer non-nullable types by default; only mark something nullable when absence is a genuinely meaningful case.
  • Validate and convert nullable external data (JSON, user input) into non-null domain types as early as possible.
  • Use ?., ?:, and safe scope functions instead of !! in almost all situations.
  • Favor an empty collection over a nullable collection so callers do not need extra null checks just to iterate.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.