skip to content

What is starProjectedType, and when would you use it instead of typeOf<T>()?

level: seniorimportance: should knowfreq 28%

answer

  1. starProjectedType: KClass -> KType with all params as *
  2. List::class.starProjectedType == List<*>
  3. Use when you only have a KClass at runtime, no real args
  4. Needs kotlin-reflect; typeOf basic use does not
  5. Result is never marked nullable

basics

~20 s

starProjectedType turns a KClass into a KType where every generic parameter is replaced by a star (*), meaning 'unknown'. You use it when you have a KClass at runtime but no concrete type arguments to fill in.

solid answer

~40 s

KClass<*>.starProjectedType is an extension property (kotlin.reflect.full) that produces a KType for that class with each type parameter replaced by a star projection (*) — e.g. List::class.starProjectedType is List<*>. You reach for it when you hold only a KClass (say, obtained dynamically from an instance via ::class) and must produce a KType, but you have no real type arguments. By contrast typeOf<T>() yields a fully resolved KType including concrete arguments and nullability, and requires the type to be statically known (often via reified). Note starProjectedType lives in kotlin-reflect's full API, so it needs the kotlin-reflect dependency, whereas basic typeOf does not. The resulting type is never marked nullable. For a class with no type parameters, starProjectedType is just that class as a KType (e.g. String::class.starProjectedType == typeOf<String>()).

code

kotlin · 10 lines
kotlin
import kotlin.reflect.full.starProjectedType
import kotlin.reflect.typeOf

fun main() {
    println(List::class.starProjectedType)            // List<*>
    println(String::class.starProjectedType == typeOf<String>()) // true (no type params)
    // From an instance you lost the argument to erasure:
    val xs: List<String> = listOf("a")
    println((xs::class).starProjectedType)            // List<*>, not List<String>
}

go deeper

for a junior

Recognizes starProjectedType fills generics with stars to make a KType from a KClass.

for a middle

Knows when to choose it over typeOf and that it loses concrete arguments.

for a senior

Explains the erasure rationale, the kotlin-reflect dependency, and never-nullable result.

for a principal

Designs reflection-based subtype/registry logic choosing between typeOf, KClass, and starProjectedType with the dependency and erasure trade-offs in mind.

## What starProjectedType produces `val KClass<*>.starProjectedType: KType` (in `kotlin.reflect.full`) returns a `KType` for the class where **every** type parameter is filled with a **star projection** (`*`). A star projection means 'some unknown type argument' — the safe, information-free stand-in. ```kotlin import kotlin.reflect.full.starProjectedType println(List::class.starProjectedType) // kotlin.collections.List<*> println(Map::class.starProjectedType) // kotlin.collections.Map<*, *> println(String::class.starProjectedType) // kotlin.String (no params -> no stars) ``` ## typeOf vs starProjectedType | | typeOf<T>() | KClass.starProjectedType | |---|---|---| | Input | a statically known type | a runtime KClass | | Type args | concrete (List<String>) | all star (List<*>) | | Nullability | as written (?) | never marked nullable | | Dependency | basic use needs only stdlib | needs kotlin-reflect | Use **typeOf** when the concrete generic type is known at compile time (often forwarded via `reified T`). Use **starProjectedType** when you only have a `KClass` discovered at runtime — for instance `value::class.starProjectedType` — and you cannot know the real type arguments because erasure already discarded them. ```kotlin import kotlin.reflect.full.starProjectedType fun typeFromInstance(value: Any): kotlin.reflect.KType = value::class.starProjectedType // best you can do from an erased instance ``` ## Why it exists Many reflection APIs (e.g. `KCallable.returnType` comparisons, `isSubtypeOf` checks via `kotlin.reflect.full`) want a `KType`, but when you start from a `KClass` you genuinely lack argument information. A star projection is the honest representation of 'this class, arguments unknown' and is also the supertype that any concrete instantiation conforms to for subtype checks. ## Gotchas - It requires **kotlin-reflect** on the classpath; calling it without that artifact throws `KotlinReflectionNotSupportedError` at runtime. - The result is never `isMarkedNullable`; you cannot recover a `?` from a bare `KClass`. - For zero-parameter classes it simply equals the plain `KType` for that class.

  • Why can't you recover List<String> from an instance via reflection?
    JVM erasure removes the argument at runtime; the instance only knows it is a List, so starProjectedType can give at best List<*>.
  • Is starProjectedType ever marked nullable?
    No. A bare KClass has no nullability information, so the produced KType is never marked nullable.

typeOf is a fully addressed envelope (List of String); starProjectedType is one addressed only to 'List, occupant unknown' (List<*>).

saying these in an interview costs you the question

  • Claiming starProjectedType recovers concrete type arguments from an instance
  • Saying it works without the kotlin-reflect dependency
  • Thinking the result can be marked nullable
  • Confusing star projection (*) with Any as an explicit argument

context