What does the star projection `List<*>` mean in Kotlin, and what can you safely do with such a value?
answer
- `*` = unknown-but-safe type argument
- Reads come back as Any? for List<*>
- Can't add a concrete element to MutableList<*>
- Kotlin's safe replacement for Java raw types
- Use when you don't depend on the concrete type
basics
~20 sList<*> means a list of some unknown element type. You can read items as Any? and call type-agnostic members like size, but you cannot safely add a specific element because the real type is unknown.
solid answer
~40 s`List<*>` is the star projection: a `List` whose element type is unknown but still type-safe. The `*` says "I don't know or care which `T` this is." You can read elements (they come back as `Any?`) and use members that don't depend on `T` in an unsafe position, like `size` or `isEmpty()`. You cannot call `add(x: T)`-style methods because the compiler can't prove `x` matches the hidden `T`. For a read-only `List`, `List<*>` behaves like `List<Any?>` for reads. It's distinct from `List<Any>` (a list known to hold `Any`) and from the raw types Kotlin doesn't have. Use it when you genuinely don't know the argument, e.g. `fun printAll(c: Collection<*>)`.
code
kotlin · 9 linesfun countItems(c: Collection<*>): Int = c.size
fun firstOrNothing(list: List<*>): Any? = list.firstOrNull()
fun main() {
println(countItems(listOf(1, 2, 3))) // 3
println(countItems(setOf("a", "b"))) // 2
println(firstOrNothing(listOf(true))) // true (typed as Any?)
}go deeper
Knows * means an unknown element type and that you can read as Any? and call size.
Explains why you can't write to a MutableList<*> and that it's Kotlin's safe alternative to raw types.
Distinguishes List<*> from List<Any?> and ties safety to projected bounds.
Frames star projection as existential typing and discusses API-design implications of accepting * vs a generic parameter.
## What the star projection is The **star projection** is written with a single asterisk as the type argument, e.g. `List<*>`, `Map<String, *>`, `Comparable<*>`. It means: *this is a generic type whose actual type argument is unknown, but I still want the result to be fully type-safe.* Think of `*` as "some specific type exists here, I just don't know which one." It is **not** `List<Any?>` declared explicitly, and Kotlin has **no raw types** like Java's `List` — `*` is the safe, principled replacement. ## What you can and cannot do For a read-only interface like `List<out E>`: - **Reads are allowed**: `val first = list[0]` gives back `Any?` (the safe upper bound of `out E`). - **`size`, `isEmpty()`, `contains(Any?)`** work because they don't put an unknown `E` into an `in` position. For a mutable type like `MutableList<E>`: - **Writes are forbidden**: `mutableList.add("x")` does **not** compile, because the compiler cannot prove `"x"` matches the hidden element type. - You can still call `removeAt`/`clear` because they don't take an `E` as input. ```kotlin fun describe(list: List<*>) { println("size = ${list.size}") // OK: no E involved val item: Any? = list.firstOrNull() // OK: reads as Any? // (list as MutableList).add(1) // not provably safe } describe(listOf(1, 2, 3)) describe(listOf("a", "b")) ``` ## Why it's safe The star projection works because Kotlin substitutes safe **bounds** for the unknown argument. The net effect: you may consume values that come *out* (typed as the upper bound) but you may not feed values *in*. ## When to reach for it Use `*` when the function truly doesn't depend on the concrete argument — counting, logging, emptiness checks, or passing through. If you DO need the type, use a generic type parameter (`fun <T> foo(list: List<T>)`) or an explicit projection (`List<out Number>`) instead.
- What type does `list[0]` have when `list: List<*>`?`Any?` — the safe upper bound for an unknown `out E`, since elements can be read but their exact type is unknown and may be nullable.
- Is `List<*>` the same as `List<Any?>`?Not exactly. `List<Any?>` is a concrete known type, while `List<*>` records that there is some specific unknown `T`. For read-only `List` the reading behavior coincides, but they are different declarations and `*` also applies safely to invariant/in positions where `Any?` would not.
Like a sealed mystery box labeled only 'contains items' — you can take things out and treat them generically, but you can't safely put a specific thing in because you don't know what's already inside.
saying these in an interview costs you the question
- Saying `List<*>` means 'a list of Any' that you can add anything to
- Confusing `List<*>` with Java raw types (claiming it's unsafe/untyped)
- Thinking you can call `add("x")` on a `MutableList<*>`
- Believing the star is just shorthand for `List<Any?>` in all positions
- Saying reads from `List<*>` return the concrete element type