Turn assumptions into explicit contracts
!! converts a nullable expression to a non-null value by throwing if it is null. It does not validate data or make the program safer. Most uses can be replaced by branching, an early return, or a contract check with an explanatory message.
fun normalizedId(raw: String?): String {
val id = requireNotNull(raw) { "A profile ID is required" }.trim()
require(id.isNotEmpty()) { "Profile ID cannot be blank" }
return id
}
requireNotNull communicates invalid caller input. Use checkNotNull when an internal state invariant is broken. For expected absence, return null or a domain result instead of throwing. These options have different meanings; replacing every !! with requireNotNull mechanically does not improve the API design.
Java platform types and initialization mistakes can still cause runtime null failures, so inspect boundaries where external values enter the program.
Exercise
Refactor a function that calls input!!.toInt() into a parser returning either a positive number or null. Then write a separate function that requires an already validated number.
Check: blank, null, and nonnumeric input should follow the parser's documented failure path without a null-pointer exception.