Null safety is Kotlin's signature feature; it shapes every API you write and read.
In Kotlin, types are non-nullable by default: a String can never hold null, and the compiler enforces it. To allow null, add ?: String?.
You can't call members on a nullable value directly. Instead:
- Safe call
s?.lengthreturns null ifsis null. Calls chain:user?.address?.city. - Elvis operator
s?.length ?: 0supplies a fallback. The right side can also bereturnorthrow:val id = param ?: return. - Smart casts: after
if (s != null), the compiler treatssasStringinside the branch (forvallocals and stable values). ?.let { ... }runs a block only when the value is non-null.- Not-null assertion
s!!throws aNullPointerExceptionifsis null. Treat each!!as a code smell.
Nullable types work with generics and collections: List<String?> (elements may be null) differs from List<String>? (the list itself may be null). Helpers include filterNotNull(), firstOrNull(), toIntOrNull(), getOrNull(i).
lateinit var declares a non-null property initialized later (common with dependency injection and test setup); accessing it before assignment throws.
At the Java boundary, types from Java are platform types (String!) with unknown nullability; annotate Java code with @Nullable/@NonNull or be defensive.
Example
fun lengthOf(s: String?): Int = s?.length ?: 0
fun shout(s: String?): String {
if (s == null) return "nothing"
return s.uppercase() // smart cast: s is String here
}
data class Address(val city: String?)
data class User(val name: String, val address: Address?)
fun main() {
println(lengthOf(null)) // 0
println(lengthOf("Kotlin")) // 6
println(shout(null)) // nothing
val u = User("Ada", Address(null))
println(u.address?.city ?: "unknown") // unknown
val maybe: String? = "42"
maybe?.toIntOrNull()?.let { println(it * 2) } // 84
val names: List<String?> = listOf("a", null, "b")
println(names.filterNotNull()) // [a, b]
val oops: String? = null
runCatching { oops!!.length }.onFailure { println(it::class.simpleName) } // NullPointerException
}Remember
- T is non-null, T? may be null, enforced at compile time
- ?. safe call, ?: Elvis fallback, ?.let for non-null blocks
- Smart casts follow null checks automatically
- !! throws NPE; avoid it
Watch out for
- Sprinkling !! to silence the compiler instead of handling the null case.