In Kotlin, what is the difference between the types String and String?, and which one can hold null?
answer
- ? is part of the type, not a flag
- Non-null value set excludes null; nullable adds null
- Non-null is the default — opt in with ?
- Compiler forbids dereferencing T? without ?., ?:, or !!
- Catches NPE at compile time
basics
~10 sString 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 sIn 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 linesfun 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 elvisgo deeper
Knows String can't be null and String? can, and that ? marks nullability.
Explains it as two distinct types with different value sets and the compiler enforcing handling.
Connects it to compile-time NPE prevention, the subtype relation, and platform types from Java.
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)