In Kotlin, what does the `out` modifier on a type parameter mean, as in `interface Source<out T>`, and how does it affect assignment between generic types?
answer
- out = producer = covariant
- List<out E> is read-only and assignable up
- subtype direction preserved: Source<Cat> <: Source<Animal>
- T allowed only in return/val positions
- annotate once at the declaration
basics
~10 sout makes the type parameter covariant: the type only produces (returns) values of T, never consumes them. So a Source<Cat> can be used where a Source<Animal> is expected, because every Cat is an Animal.
solid answer
~40 sDeclaring `interface Source<out T>` marks T as covariant. It tells the compiler T appears only in `out` positions — return types of functions and `val` properties — so the class is a pure producer of T. Because of that safety guarantee, subtyping is preserved in the same direction as T: if `Cat` is a subtype of `Animal`, then `Source<Cat>` is a subtype of `Source<Animal>`. Kotlin's `List<out E>` is the canonical example: `List<String>` is assignable to `List<Any>`. The compiler enforces this once at the declaration (declaration-site variance), so every call site benefits without extra annotations. If you tried to add `fun consume(item: T)`, the compiler rejects it because T would then appear in an `in` position.
code
kotlin · 10 linesinterface Source<out T> {
fun next(): T
}
fun useAnimals(src: Source<Animal>) { /* ... */ }
val catSource: Source<Cat> = object : Source<Cat> {
override fun next() = Cat()
}
useAnimals(catSource) // works: Source<Cat> is a subtype of Source<Animal>go deeper
Knows out = covariant producer and that List<String> is usable as List<Any>.
Explains the out-position restriction and why MutableList can't be covariant.
Articulates declaration-site vs use-site and soundness reasoning behind the position check.
Connects variance to API design — choosing read-only covariant interfaces to widen accepted types across a public surface.
## What `out` declares `out` on a type parameter means **covariance**. Read it literally: the type parameter is only used as **out**put — values of that type come *out of* the object (return values), they are never passed *in*. ```kotlin interface Source<out T> { fun next(): T // T in an OUT position (return) — allowed // fun put(item: T) // T in an IN position (parameter) — COMPILE ERROR } ``` ## Why this enables subtyping Normally generics are **invariant**: `Box<Cat>` and `Box<Animal>` are unrelated types even though `Cat : Animal`. Variance changes that. With `out`, subtyping flows in the **same** direction as the type argument: - `Cat` is a subtype of `Animal` - therefore `Source<Cat>` **is a subtype of** `Source<Animal>` ```kotlin val cats: Source<Cat> = ... val animals: Source<Animal> = cats // OK because of `out` val a: Animal = animals.next() // safe: a Cat IS an Animal ``` This is sound because everything you can get out of `animals` is at least an `Animal`. ## The standard library example `kotlin.collections.List` is declared `public interface List<out E>`. That is why this compiles: ```kotlin val strings: List<String> = listOf("a", "b") val anys: List<Any> = strings // List<String> <: List<Any> ``` `List` is read-only (no `add`), so E never appears in an `in` position — covariance is safe. `MutableList<E>` is **invariant** (no `out`) precisely because `add(e: E)` consumes E. ## Declaration-site The key Kotlin idea: you write `out` **once**, at the class/interface declaration. Every use of `Source<…>` then automatically gets covariant behavior. The compiler verifies, at the declaration, that T is only ever used in safe (out) positions. ## Terms - **Covariance**: subtyping preserved (`A : B` ⇒ `G<A> : G<B>`). - **Producer**: a type that only returns T. - **out position**: function return type, `val` property type.
- Why is `MutableList<E>` not declared with `out`?Because `add(element: E)` and `set(index, element: E)` put E into an `in` position. Marking it `out` would let you treat a `MutableList<Cat>` as `MutableList<Animal>` and add a Dog, breaking type safety.
- Can a covariant `out` type have a function parameter of type T at all?Not in a normal parameter position. The compiler rejects T in `in` positions. You can sometimes work around with `@UnsafeVariance`, but that disables the safety check and is rarely appropriate.
An out type is like a vending machine you can only take items from: if it dispenses Cats, it certainly dispenses Animals.
saying these in an interview costs you the question
- Saying `out` means the parameter is optional or nullable
- Claiming `out` makes `Source<Animal>` a subtype of `Source<Cat>` (direction reversed)
- Thinking `out` is only a runtime hint with no compile-time effect
- Believing you can still pass T as a function argument in an `out` class
- Confusing `out` with the `out` keyword for output function parameters from other languages