skip to content

What is the type of the literal `null`, and how do Nothing and Nothing? behave with generics like emptyList()?

level: seniorimportance: should knowfreq 45%

answer

  1. `null` literal : Nothing?
  2. Nothing? has one value (null); Nothing has zero
  3. emptyList() = single List<Nothing> object
  4. Covariance + bottom => reusable empty collections
  5. Inferred Nothing/Nothing? => annotate the type

basics

~10 s

The bare null literal has type Nothing?, the nullable bottom type whose only value is null. Because Nothing is below everything, an empty list typed List<Nothing> can stand in for a list of anything.

solid answer

~40 s

`null` by itself has type `Nothing?` — `Nothing` made nullable, inhabited only by `null`. Since `Nothing?` is a subtype of every nullable type `T?`, `null` is assignable to any nullable variable. For non-nullable bottom: `Nothing` is a subtype of all types, so a value that never exists fits anywhere. With covariant generics this is powerful: `emptyList<Nothing>()` (what `emptyList()` returns) is `List<Nothing>`, and because `List` is declared `out E`, `List<Nothing> <: List<T>` for any `T`. So one shared empty-list instance serves every element type. The compiler infers `Nothing` (or `Nothing?`) as a type argument when a generic value has no constraining info, e.g. `val xs = mutableListOf<Nothing?>()` after only adding nulls, which often signals you should specify the type explicitly to avoid an over-narrow `Nothing`-typed result.

code

kotlin · 6 lines
kotlin
val s: String? = null          // null : Nothing?  <: String?

val names: List<String> = emptyList()  // List<Nothing> reused as List<String>
val ids:   List<Int>    = emptyList()  // same EmptyList singleton

val inferred = listOf(null)    // List<Nothing?> -- usually annotate instead

go deeper

for a junior

Knows null can go into any nullable variable but may not name Nothing? as its type.

for a middle

States null : Nothing? and that Nothing? has only the null value.

for a senior

Explains the emptyList()/List<Nothing> covariance trick and inference pitfalls clearly.

for a principal

Reasons about why covariance makes the shared empty-collection sound while invariance forbids it for MutableList, and the soundness of bottom + variance.

## The type of `null` The literal `null` has type **`Nothing?`** — the nullable version of the bottom type. `Nothing?` is inhabited by exactly **one** value: `null`. Because `Nothing` is a subtype of every type `T`, `Nothing?` is a subtype of every nullable type `T?`. That is precisely why you can assign `null` to a `String?`, `Int?`, `List<User>?`, etc. ```kotlin val a: String? = null // Nothing? <: String? val b: Int? = null // Nothing? <: Int? ``` ## Nothing vs Nothing? - **`Nothing`** — zero values; used for never-returning code (`throw`, `TODO()`). - **`Nothing?`** — one value, `null`; used as the type of the bare `null` literal and for "only ever null" flow positions. ## Interaction with covariant generics Kotlin's `List<out E>` is **covariant**: `List<A> <: List<B>` when `A <: B`. Combine that with `Nothing` being the bottom: ```kotlin public fun <T> emptyList(): List<T> = EmptyList // EmptyList : List<Nothing> ``` `emptyList()` is backed by a single object `EmptyList : List<Nothing>`. Because `Nothing <: T` for every `T`, and `List` is covariant, `List<Nothing> <: List<T>` — so the **same** immutable empty-list instance is reusable as `List<String>`, `List<Int>`, anything. The same trick powers `emptySet()`, `emptyMap()`, and the singleton empty sequence. ```kotlin val names: List<String> = emptyList() // List<Nothing> seen as List<String> val ids: List<Int> = emptyList() // same EmptyList object ``` ## Inference pitfalls When the compiler has no information to pin a type parameter, it may infer `Nothing` or `Nothing?`: ```kotlin val xs = listOf(null) // List<Nothing?> xs.add("x") // (if mutable) would not compile: element type is Nothing? ``` Seeing `Nothing`/`Nothing?` in an inferred type is usually a hint to **annotate explicitly** (e.g. `mutableListOf<String?>()`), because a `MutableList<Nothing>` can hold nothing you can construct. ## Why invariant generics differ For an **invariant** type like `MutableList<E>`, `MutableList<Nothing>` is *not* a subtype of `MutableList<String>` — variance is the reason the empty-list trick works for read-only `List` but not for mutable collections. That is also why `emptyList()` returns a read-only `List`, never a `MutableList`.

  • Why can the same emptyList() instance serve every element type, but emptyMutableList can't be shared the same way?
    List<out E> is covariant, so List<Nothing> <: List<T>. MutableList<E> is invariant (it both produces and consumes E), so MutableList<Nothing> is not a subtype of MutableList<T>; sharing a mutable one would be unsound.
  • What is the difference in inhabitants between Nothing and Nothing??
    Nothing has zero inhabitants; Nothing? has exactly one, the value null.

A List<Nothing> is a vending machine guaranteed empty — safe to label as 'snacks', 'drinks', or anything, because it will never actually dispense a wrong item.

saying these in an interview costs you the question

  • Saying null has type Any? or no type
  • Claiming Nothing and Nothing? have the same number of values
  • Asserting emptyList() returns a MutableList
  • Not connecting the empty-list trick to out-variance
  • Thinking MutableList<Nothing> is a subtype of MutableList<T>

context