skip to content

Nullable Types

How the nullable/non-null split is expressed across the type system: plain declarations, generic type parameters, extension receivers, and the definitely-non-null form. This is where null safety stops being a syntax trick and becomes a typing question.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

explore

questions

20

In Kotlin, what is the default upper bound of an unbounded type parameter like <T>, and what does that imply about whether T can hold null?

level: juniorimportance: must knowfreq 70%

answer

  1. Unbounded <T> => upper bound Any?
  2. Any? = Any + null
  3. <T : Any> forbids null
  4. T inside body may be null
  5. Caller can pass Box<String?>

basics

~10 s

An unbounded <T> defaults to the upper bound Any?, which includes null. So T can hold a null value unless you restrict it.

solid answer

~40 s

When you write a generic like class Box<T> or fun <T> id(x: T): T, the type parameter T has no explicit upper bound, so Kotlin uses the implicit upper bound Any? (the type at the top of the nullable hierarchy). Because Any? includes null, T is implicitly nullable: a caller can supply Box<String?> or call id(null), and inside the generic code a value of type T may be null. This is why you cannot safely call members like x.length on a T without a null check or a smart-cast. To forbid nulls you constrain the parameter with <T : Any>, making the upper bound the non-null Any. The distinction matters because T (the parameter itself) and T? (an explicitly nullable use of T) behave differently in generic code.

code

kotlin · 6 lines
kotlin
class Box<T>(val value: T)        // T : Any? implicitly
val nullable = Box<String?>(null)  // legal

fun <T : Any> nonNullBox(v: T) = Box(v)
nonNullBox("x")                    // OK
// nonNullBox(null)               // compile error

go deeper

for a junior

Knows unbounded <T> defaults to Any? and that null is therefore allowed.

for a middle

Can show how <T : Any> forbids null and changes body assumptions, with a code example.

for a senior

Explains why the compiler treats T as possibly-null and the interaction with smart-casts and member access.

for a principal

Frames the default Any? bound as a deliberate language design choice and discusses API design implications for library type parameters.

## The implicit upper bound Every generic type parameter in Kotlin has an **upper bound** — the most general type it is allowed to be. When you declare a parameter without a bound, like `class Box<T>` or `fun <T> identity(x: T): T`, Kotlin fills in the implicit upper bound `Any?`. `Any?` is the root of Kotlin's *nullable* type hierarchy — it is `Any` (the non-null root) plus `null`. Because the bound is `Any?`, **a caller may substitute a nullable type for `T`**, and `null` itself is a legal value of `T`. ```kotlin class Box<T>(val value: T) val a = Box("hi") // T = String val b = Box<String?>(null) // T = String?, value is null — legal ``` ## Why T is treated as possibly-null inside the body Inside generic code, the compiler must assume `T` could be a nullable type, so a value of type `T` is treated as possibly null. You cannot dereference it without handling null: ```kotlin fun <T> firstChar(x: T): Char { // return x.length // ERROR: x may be null, and T isn't even necessarily String return x.toString().first() // toString() is on Any?, returns "null" for null } ``` Even the safe-call operator `?.` is meaningful here because `x` of type `T` might be null. ## Forbidding null with `<T : Any>` To say "T must be a non-null type," add the explicit bound `Any`: ```kotlin fun <T : Any> requireNonNull(x: T): T = x requireNonNull("hi") // OK, T = String // requireNonNull(null) // ERROR: null does not satisfy T : Any ``` Now callers cannot pass `Box<String?>` and cannot pass a literal `null`; inside the body `x` is known non-null. ## Key takeaways - Unbounded `<T>` == `<T : Any?>` == possibly nullable. - `<T : Any>` forbids null and makes `T` non-null inside the body. - This is purely about the *parameter*; an explicit `T?` is a separate, always-nullable use discussed elsewhere.

  • What does <T : Any> change inside the function body?
    T is known to be non-null, so values of type T can be dereferenced without a null check and smart-casts treat them as non-null.
  • Is <T> the same as <T : Any?>?
    Yes — they are equivalent; the explicit Any? bound just spells out the default.

An unbounded <T> is like a job posting with no requirements — 'null' applicants are welcome until you add 'must be non-null' (: Any).

saying these in an interview costs you the question

  • Claiming unbounded <T> defaults to Any (non-null)
  • Saying T can never be null without a bound
  • Confusing the bound Any? with the wildcard/star projection
  • Thinking <T : Any> changes the runtime type erasure behavior

context

open as a page

What is a nullable receiver in Kotlin, and how does it let String?.isNullOrEmpty() be called on a null value without a NullPointerException?

level: juniorimportance: must knowfreq 70%

basics

~20 s

It is an extension function written on a type that may be null (like String?). Because the function is defined for the null case too, you can call it on null, and inside it checks whether this is null.

open as a page

In Kotlin, what is the difference between the types String and String?, and which one can hold null?

level: juniorimportance: must knowfreq 92%

basics

~10 s

String can never be null; the compiler rejects null for it. String? can hold either a real String or null. The ? means 'this might be null'.

open as a page

Show how `T & Any` is used to override a Java generic method annotated `@NotNull`. Why is it needed there?

level: middleimportance: must knowfreq 30%

basics

~20 s

When a Java method returns a generic value marked @NotNull, Kotlin couldn't express that the return is non-null in an override. T & Any lets the Kotlin override say 'this return is definitely not null'.

open as a page

Inside generic code, how does using T differ from using T? when the parameter is declared <T : Any>? Show how each affects what values are accepted and returned.

level: middleimportance: must knowfreq 60%

basics

~10 s

With <T : Any>, T is always non-null, while T? adds nullability back for that specific use. So T rejects null but T? accepts it, even when T itself cannot be null.

open as a page

Inside an extension function declared on String?, what is the type of `this`, and how do you safely call String members on it?

level: middleimportance: must knowfreq 55%

basics

~20 s

Inside the body, this is nullable (String?). To use String members you must first check it is not null; after that check the compiler treats this as non-null, so you can call methods like length.

open as a page

What does the type `T & Any` mean in Kotlin, and what problem does it solve?

level: juniorimportance: should knowfreq 35%

basics

~10 s

T & Any means "the type T, but guaranteed not null." If a generic type T could be null, writing T & Any forces it to be a non-null version of that type.

open as a page

When should you use `T & Any` versus just bounding the type parameter with `T : Any`? How do they differ?

level: middleimportance: should knowfreq 22%

basics

~20 s

T : Any forces every use of T to be non-null for all callers. T & Any keeps T able to be nullable but marks one specific spot (a parameter, return, or local) as non-null.

open as a page

Implement a generic extension fun <T> Iterable<T>.firstMatchingOrNull(predicate: (T) -> Boolean): T?. Explain why the return is T? and how it behaves when T is itself a nullable type.

level: middleimportance: should knowfreq 45%

basics

~20 s

Loop over the elements, return the first that matches, else null. The return is T? because there may be no match. If T is nullable, T? is still just that nullable type, so a matched null is indistinguishable from 'no match'.

open as a page

Explain the output of `val s: String? = null; println(s?.isNullOrEmpty())` versus `println(s.isNullOrEmpty())`. Why do they differ?

level: middleimportance: should knowfreq 25%

basics

~10 s

Writing s?.isNullOrEmpty() prints null, because the safe call skips the function when s is null. Writing s.isNullOrEmpty() prints true, because the function is built to run on null and reports it as empty.

open as a page

Explain the subtype relationship between T and T? in Kotlin. Which direction of assignment is allowed and why?

level: middleimportance: should knowfreq 64%

basics

~10 s

T is a subtype of T?, so a non-null value fits anywhere a nullable is expected. The other way doesn't work without a check, because a nullable might actually be null.

open as a page

Explain the precise semantics of `T & Any`: what is its erasure, why does the compiler sometimes warn, and where is it disallowed?

level: seniorimportance: should knowfreq 14%

basics

~20 s

T & Any is a compile-time-only non-null version of T. At runtime it erases to T's bound. The compiler warns when it's redundant (T already non-null) and forbids it where T isn't a type parameter with a nullable bound.

open as a page

When a generic Kotlin function calls into Java, how do platform types affect nullability of T, and how can <T : Any> versus an unbounded <T> change the safety guarantees you actually get?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Java values arrive as platform types with unknown nullability, so the compiler can't enforce non-null. Declaring your parameter <T : Any> doesn't check Java-supplied nulls at the boundary — a null can slip in and fail later. Explicit checks are needed.

open as a page

How does the compiler decide between a member call and a nullable-receiver extension, and how does that explain calling an extension on null?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Extensions are picked at compile time from the declared type, while members are looked up on the real object at runtime. Because an extension call just passes the value as an argument, the value can be null, unlike a member call which needs a real object.

open as a page

When should you design an extension function with a nullable receiver T? rather than a non-null receiver, and what are the trade-offs?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Use a nullable receiver when the function gives a sensible answer for null too — like turning null into an empty string. Avoid it when null has no meaning for the operation, so callers are forced to handle null themselves.

open as a page

What is the type of the null literal in Kotlin, and how does Nothing? relate to nullable types?

level: seniorimportance: should knowfreq 38%

basics

~10 s

The null literal has type Nothing?. Nothing has no values at all, so Nothing? has exactly one value: null. That's why null can be assigned to any nullable type.

open as a page

When designing a generic API, when should you constrain <T : Any> versus accepting an unbounded <T> and using T? at specific positions? Discuss the trade-offs and a case where each is the right call.

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

Use <T : Any> when nulls make no sense for your type and you want callers blocked from passing them. Use unbounded <T> when nullable element types are legitimate, and mark only the positions that can be null with T?.

open as a page

Do T and T? differ at the JVM bytecode level, and what are the consequences for nullability enforcement at runtime?

level: seniorimportance: nice to knowfreq 26%

basics

~20 s

For object references, T and T? look the same at runtime — nullability is a compile-time rule. The compiler adds runtime null checks at public boundaries so callers from Java or reflection still fail fast.

open as a page

As a library author exposing a generic API consumed from both Kotlin and Java, when would you reach for `T & Any`, and what are the design and migration consequences?

level: principalimportance: nice to knowfreq 8%

basics

~20 s

Use T & Any when you must keep T flexible (often for Java overrides) but a specific input or output is always non-null. It documents the contract precisely without forcing all callers to drop nullable types.

open as a page

Kotlin makes non-null the default and requires opting into nullability with ?. What are the design trade-offs of this choice compared with treating every reference as nullable?

level: principalimportance: nice to knowfreq 18%

basics

~10 s

Making non-null the default means most values are guaranteed safe, so bugs surface at compile time. The cost is more friction at boundaries (like Java code) where the compiler can't prove nullability.

open as a page