Null Safety Best Practices
In this page:
val name: Type = value // prefer non-null
val parsed = input?.toIntOrNull() ?: default
fun functionName(): List<Type> { // empty list instead of null
return emptyList()
}
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
// Define a data class `Product`
data class Product(val name: String, val price: Double, val discountCode: String? = null)
// Entry point: execution of the program starts here
fun main() {
// Declare a read-only variable `product`, set to `Product("Book", 15.0)`
val product = Product("Book", 15.0)
// Print "${product.name}: $${product.price}, discount=${product.discountCode ?: "none"}" to the console, with a trailing newline
println("${product.name}: $${product.price}, discount=${product.discountCode ?: "none"}")
}
Login to try C/C++/Java/PHP code in the editor
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
// Define the function `parseAge` taking `input` and returning an Int
fun parseAge(input: String?): Int {
// Return `input?.toIntOrNull() ?: error("Invalid or missing age: $input")` from this function
return input?.toIntOrNull() ?: error("Invalid or missing age: $input")
}
// Entry point: execution of the program starts here
fun main() {
// Print "Age: ${parseAge("42")}" to the console, with a trailing newline
println("Age: ${parseAge("42")}")
}
Login to try C/C++/Java/PHP code in the editor
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.
null for representing 'no results'.Example: Avoid Nullable Collections
// Define the function `findMatches` taking `query`, `items` and returning a `List<String>`
fun findMatches(query: String, items: List<String>): List<String> {
// Return `items.filter { it.contains(query) }` from this function
return items.filter { it.contains(query) }
}
// Entry point: execution of the program starts here
fun main() {
// Declare a read-only variable `results`, set to `findMatches("kt", listOf("main.kt", "readme.md"))`
val results = findMatches("kt", listOf("main.kt", "readme.md"))
// Print "Found ${results.size} matches: $results" to the console, with a trailing newline
println("Found ${results.size} matches: $results")
}
Login to try C/C++/Java/PHP code in the editor
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
// Entry point: execution of the program starts here
fun main() {
// Declare a read-only variable `cache`, set to `HashMap<String, String>()`
val cache = HashMap<String, String>()
cache["greeting"] = "Hello"
// Check whether `"greeting" in cache`
if ("greeting" in cache) {
// Print `cache["greeting"]!!` to the console, with a trailing newline
println(cache["greeting"]!!)
}
}
Login to try C/C++/Java/PHP code in the editor
- Designing APIs that return nullable types when an empty collection or a sensible default would communicate the same information more safely.
- Sprinkling
!!throughout a codebase as a quick fix instead of addressing why a value might unexpectedly be null in the first place. - Not validating external data (network responses, user input) at the boundary of your program, letting nulls leak deep into business logic.
- 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: