What happens at runtime when you cast a `List<T>` returned by `listOf(...)` back to `MutableList<T>` and call `add`? Contrast that with a list created by `mutableListOf(...)`.
answer
- cast compiles, runtime class decides
- listOf -> unmodifiable, add throws UOE
- mutableListOf -> real ArrayList, add works
- emptyList() = shared EmptyList singleton
- need mutable? copy with toMutableList(), don't cast
basics
~20 sIt depends on the real object. listOf may give an unmodifiable instance that throws when you try to add. mutableListOf gives a real ArrayList, so the cast-and-add succeeds. Both compile, but casting back is unsafe.
solid answer
~40 sCasting `List` to `MutableList` is an *unchecked* up-the-hierarchy cast that always compiles, but its runtime behavior depends on the concrete class. `listOf(1)` returns a `SingletonList`; `listOf(1,2)` returns an effectively-unmodifiable wrapper around an array — calling `add` after casting throws `UnsupportedOperationException`. `mutableListOf(...)` returns a `java.util.ArrayList`, so the cast succeeds and `add` works, mutating the object. `emptyList()` / `listOf()` return a shared `EmptyList` singleton that also rejects mutation. The lesson: the read-only/mutable distinction is enforced only by the *type system*, not the *runtime class*; relying on cast-back is brittle and a code smell. This is exactly why you copy with `toMutableList()` when you genuinely need a mutable list.
code
kotlin · 10 linesval ro = listOf(1, 2, 3)
val mu = mutableListOf(1, 2, 3)
(mu as MutableList<Int>).add(4) // [1, 2, 3, 4]
try {
(ro as MutableList<Int>).add(4)
} catch (e: UnsupportedOperationException) {
println("listOf is not really mutable")
}go deeper
Knows mutating a listOf result can fail and mutableListOf is the mutable one.
Explains the cast compiles but throws UnsupportedOperationException for listOf, succeeds for mutableListOf.
Knows the concrete classes (EmptyList, array-backed wrapper, ArrayList) and why behavior is implementation-defined.
Articulates the design: compile-time-only contract over reused Java collections, interop implications, and why cast-back is a forbidden pattern in reviews.
## The cast compiles, the runtime decides `List` and `MutableList` are *different interfaces* but the same objects implement both at runtime in many cases. Kotlin lets you write `list as MutableList`, but this is a downcast the compiler cannot verify, so it only checks at runtime whether the concrete object implements `MutableList`. ```kotlin val a = listOf(1, 2, 3) // returns Arrays.ArrayList-like, unmodifiable val b = mutableListOf(1, 2, 3) // returns java.util.ArrayList (b as MutableList).add(4) // OK -> [1,2,3,4] (a as MutableList).add(4) // throws UnsupportedOperationException ``` ## What `listOf` actually returns - `listOf()` / `emptyList()` → `EmptyList` (a Kotlin object singleton). Any mutation throws. - `listOf(x)` → a single-element list. Mutation throws. - `listOf(x, y, ...)` → backed by an array via `Arrays.asList`-style wrapper; *fixed size*, mutation throws `UnsupportedOperationException`. So even though the cast may *succeed* (the object can implement `MutableList`), the mutator methods are stubbed to throw. ## What `mutableListOf` returns A real `java.util.ArrayList`. Casting it to `List` and back to `MutableList` round-trips to the same mutable object, and `add`/`remove` work. ## Why this matters The read-only vs mutable split is a **compile-time** contract. The JVM has no separate immutable-list type in the stdlib, so Kotlin reuses Java collections under the hood. Casting back deliberately defeats the contract and produces code whose correctness depends on an undocumented concrete class — it can pass in tests (where you happened to use `mutableListOf`) and explode in production (where a `listOf` flowed in). ## The right tool If you need a mutable copy, do not cast — copy: ```kotlin val m = someReadOnly.toMutableList() // always a fresh, real ArrayList m.add(4) // always works, never aliases ``` ## Terms - **Unchecked cast**: a cast the compiler can't prove; verified (partially) at runtime. - **`UnsupportedOperationException`**: thrown by fixed/unmodifiable collections on mutation. - **`EmptyList`**: shared empty-list singleton returned by `emptyList()`.
- Why does the cast `as MutableList` compile at all if `listOf` is read-only?Because the JVM object often does implement `MutableList`; the cast type-checks against the runtime class, not the read-only declared type. The mutator methods may still throw.
- Is relying on a `ClassCastException` here a safe contract?No — sometimes you get `UnsupportedOperationException` (object implements MutableList but stubs mutators), sometimes `ClassCastException`. Behavior is implementation-defined; never depend on it.
saying these in an interview costs you the question
- Assuming `listOf(...) as MutableList` always works
- Assuming it always throws `ClassCastException`
- Treating cast-back as a legitimate way to get a mutable list
- Not knowing `mutableListOf` is a `java.util.ArrayList`
- Believing Kotlin stdlib has a distinct runtime immutable type