Skip to main content
Type projection in Kotlin is a use-site variance mechanism that restricts the type signature of a generic class or interface at the point of its usage. By applying variance modifiers (in or out) or the star projection (*) to type arguments in variable declarations or function parameters, developers can safely accept generic types with differing type parameters, effectively overriding the generic type’s default invariance. Unlike declaration-site variance (where modifiers are applied to the class definition itself, e.g., class Box<out T>), type projection occurs where the type is instantiated or referenced.

Covariant Projection (out)

Applying the out modifier at the use-site creates a covariant projection. It restricts the generic object to act strictly as a producer.
  • Mechanics: The compiler allows calling methods that return the generic type T, but prohibits calling any methods that take T as a parameter.
  • Type Safety: It guarantees that the object will only emit instances of T (or its subtypes) and prevents the injection of incompatible types.

Contravariant Projection (in)

Applying the in modifier at the use-site creates a contravariant projection. It restricts the generic object to act strictly as a consumer.
  • Mechanics: The compiler allows calling methods that take the generic type T as a parameter, but restricts the type returned when reading from the object.
  • Type Safety: It guarantees that the object can safely consume instances of T (or its subtypes). Because the exact type argument passed to the generic class is unknown at the use-site, the compiler cannot safely return T. Instead, reading from an in-projected type returns the declared upper bound of the generic type parameter. If no explicit upper bound is defined at the declaration site, it defaults to Any?.

Star Projection (*)

Star projection is used when the exact type argument is unknown or irrelevant, but type safety must still be maintained. It provides a safe approximation of the generic type based on its declaration.
  • Mechanics:
    • For reading (producing), Foo<*> acts like Foo<out TUpper>, where TUpper is the upper bound of the generic parameter (defaults to Any?).
    • For writing (consuming), Foo<*> acts like Foo<in Nothing>, meaning you cannot write any value to it.
  • Type Safety: It prevents unsafe writes entirely while allowing safe, generalized reads.

Type Projection vs. Invariance

By default, generic types in Kotlin are invariant. Type projection explicitly narrows the capabilities of the type to achieve subtyping flexibility.
Tired of Poor Kotlin Skills? Fix That With Deep Grasping!Learn More