skip to content

Nullable vs Non-Null Types

T rejects null at compile time and T? adds null to the value set, with T a subtype of T? and Nothing? the type of the null literal. Interviewers start here because every other null-safety feature is a consequence of this one distinction.

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

questions

5

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

level: juniorimportance: must knowfreq 92%

answer

  1. ? is part of the type, not a flag
  2. Non-null value set excludes null; nullable adds null
  3. Non-null is the default — opt in with ?
  4. Compiler forbids dereferencing T? without ?., ?:, or !!
  5. Catches NPE at compile time

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'.

solid answer

~40 s

In Kotlin, types are split into non-null and nullable. String is a non-null type: its value set is all strings, and assigning null is a compile-time error. String? is the nullable version: its value set is all strings plus null. The trailing ? is part of the type, not an annotation. Because of this, the compiler forces you to handle the null case before calling members on a String? — you can't just write s.length on a String?; you need s?.length, a null check, or !!. This compile-time enforcement is Kotlin's defense against NullPointerException: the type system tracks nullability so most NPEs are caught at compile time rather than at runtime. Non-null is the default, so you opt into nullability explicitly by adding ?.

code

kotlin · 6 lines
kotlin
fun len(s: String): Int = s.length        // safe, never null
fun lenOrNull(s: String?): Int? = s?.length  // must use ?.

val x: String? = null
// val y: String = x   // compile error
val y: String = x ?: ""  // ok via elvis

go deeper

for a junior

Knows String can't be null and String? can, and that ? marks nullability.

for a middle

Explains it as two distinct types with different value sets and the compiler enforcing handling.

for a senior

Connects it to compile-time NPE prevention, the subtype relation, and platform types from Java.

for a principal

Frames non-null-by-default as a deliberate language design choice and discusses interop trade-offs with platform types.

## The core distinction Every Kotlin type comes in two flavors: - **Non-null type** (`String`, `Int`, `User`): the value set contains only real instances. `null` is **not** a valid value, and the compiler rejects it at compile time. - **Nullable type** (`String?`, `Int?`, `User?`): the value set is all instances **plus** `null`. The trailing `?` is part of the type itself — `String` and `String?` are two **different** types, not a String with a flag. ```kotlin val a: String = "hi" // ok val b: String = null // COMPILE ERROR: null cannot be a value of non-null String val c: String? = "hi" // ok val d: String? = null // ok ``` ## Why it matters: compile-time NPE prevention Because the type carries nullability, the compiler knows when a value *might* be null and forces you to deal with it before dereferencing: ```kotlin val s: String? = maybeGet() // s.length // COMPILE ERROR: must be safe (?.) or non-null (!!) val n: Int? = s?.length // safe call ?., result is Int? val m: Int = s?.length ?: 0 // elvis ?: provides a fallback ``` Non-null `String` has no such restriction — `s.length` just works. ## Non-null is the default You **opt in** to nullability by writing `?`. A bare type is non-null. This flips the default compared to Java, where every reference is implicitly nullable. ## Member access summary - `?.` (safe call) — returns the member result or `null`. - `?:` (Elvis operator) — supplies a fallback when the left side is `null`. - `!!` (not-null assertion) — asserts non-null; throws `NullPointerException` if it's actually null. ## Platform types caveat Values coming from Java have **platform types** (written `String!` in errors), where the compiler can't prove nullability and relaxes checks. That's a separate concern — within pure Kotlin, `T` vs `T?` is strictly enforced.

  • Can you assign a String to a String? variable?
    Yes. String is a subtype of String?, so a non-null value is always a valid nullable value. The reverse (String? to String) requires a check or !!.
  • What happens if you call !! on a value that is actually null?
    It throws a NullPointerException at that point — !! converts a compile-time guarantee you skipped into a runtime crash.

A non-null type is a box guaranteed to contain something; a nullable type is a box that might be empty.

saying these in an interview costs you the question

  • Saying String? is 'just String with a null flag' rather than a distinct type
  • Claiming you can call .length directly on a String?
  • Thinking nullable is the default in Kotlin
  • Confusing ?. (safe call) with !! (assertion)

context

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

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

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

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