liveBook · Manning

Star projection

As explained in Kotlin in Action, 2E

A star projection is Kotlin syntax that represents a generic type argument whose specific type is unknown while preserving safe variance information.

Star projection, written `*`, represents a generic argument whose exact type is unknown or irrelevant. Use it when the generic container or interface matters but its particular type argument does not. For example, `List<*>` lets code recognize a list at runtime without claiming that it is a `List<String>` or `List<Int>`—information erased from generic types at runtime. section 11.2.1 section 11.4

Read unknown elements safely

```kotlin fun printFirst(list: List<*>) { if (list.isNotEmpty()) { println(list.first()) } } ``` `list.first()` is safe because an element from an unknown list can be viewed as `Any?`. The function can accept lists with different element types, while making no promise about which element type any caller supplied. section 11.3.6

For mutable invariant collections, unknown is not the same as broad. `MutableList<Any?>` is known to accept values of any type, so code may add to it. `MutableList<*>` was created for one specific element type that is hidden from the current code; adding `42`, or any other value, could break that original expectation. Kotlin therefore allows reads but rejects writes. section 11.3.6

Unknown mutable list

```kotlin val values: MutableList<*> = if (Random.nextBoolean()) mutableListOf('a', 1) else mutableListOf('a', 'b') println(values.first()) // values.add(42) // error ``` The star projection acts as `MutableList<out Any?>`: it exposes produced values as `Any?` but prohibits supplying an element. section 11.3.6

Use star projections in these practical situations:

  • Check a generic class at runtime when only its base type is knowable: `if (value is List<*>) { ... }`. section 11.2.1
  • Store differently typed generic implementations behind a common registry type, such as `MutableMap<KClass<*>, FieldValidator<*>>`. section 11.3.6
  • Describe annotation class references whose generic implementation type is constrained but whose argument varies: `KClass<out ValueSerializer<*>>`. section 12.1.7

A star-projected consumer is deliberately hard to call. With `FieldValidator<in T>`, `FieldValidator<*>` hides the input type, so `validate(...)` is rejected: code cannot know what value the validator accepts. Recovering a specific `FieldValidator<String>` requires an explicit cast, which can fail later if the registry entry actually validates another type. Encapsulate such unchecked casts behind typed registration and lookup APIs where possible. section 11.3.6

Star projection corresponds to Java’s unbounded wildcard form, `MyType<?>`. It is not a replacement for a useful type parameter: if a function can preserve the caller’s element type, a generic signature such as `fun <T> printFirst(list: List<T>)` retains more type information than `List<*>`. Choose `*` when that information is genuinely unavailable or unnecessary. section 11.3.6

import kotlin.random.Random
 
fun main() {
    val list: MutableList<Any?> = mutableListOf('a', 1, "qwe")
    val chars = mutableListOf('a', 'b', 'c')
    val unknownElements: MutableList<*> =                   #1
            if (Random.nextBoolean()) list else chars
    println(unknownElements.first())                        #2
    // a
    unknownElements.add(42)                                 #3
    // Error: Out-projected type 'MutableList<*>' prohibits
    // the use of 'fun add(element: E): Boolean'
}

Compare a mutable list known to accept `Any?` with a star-projected list that can be read but not written. section 11.3.6

How Kotlin in Action, 2E covers it

How deep it goes
Explains runtime checks, read/write restrictions, variance behavior, casts, registries, and annotation class parameters.
Examples
Demonstrates `List<*>` checks, mutable-list reads, validator registries, explicit casts, and serializer annotation parameters.
What it assumes
Assumes familiarity with Kotlin generic types and introduces star syntax through lists, variance, and casts.
What you can do after
Apply it to inspect, store, and constrain generic values without falsely claiming a specific type argument.
How it connects
Connects star projections to type erasure, variance, casts, Java wildcards, `KClass`, and annotations.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Kotlin in Action, 2E. The section links point to the parts of the book it draws on.