Advanced 22 min readModule: Module 6: Generics, Declaration-Site Variance (in/out) & Reified Types
Generics, Variance (in/out) & Reified Type Parameters
Master type safety in generic architectures: declaration-site variance (`out` for producers, `in` for consumers) and inline reified type parameters.
What You Will Learn in This Lesson
- Declaration-site variance: Covariance (`out T`) and Contravariance (`in T`)
- Why Kotlin's declaration-site variance is superior to Java wildcard types (`? extends T`, `? super T`)
- Type erasure on the JVM and how `inline` + `reified` retains generic type information at runtime
- Building type-safe repositories and dependency injection locators
Introduction & Core Concept
Generics allow classes and functions to operate on parameterized types with complete compile-time type safety. Unlike Java which uses complex use-site wildcards (? extends T), Kotlin introduces Declaration-Site Variance: using 'out' for Producers (covariant) and 'in' for Consumers (contravariant). Furthermore, Kotlin solves JVM type erasure using 'inline reified' types.
WHY DOES THIS MATTER IN THE REAL WORLD?
Understanding variance is essential when designing libraries, reactive architectures, and dependency injection frameworks. Reified types allow developers to inspect generic classes directly at runtime without passing awkward Class<T> parameters.
Syntax & Structure
kotlin
interface Producer<out T> { fun produce(): T }interface Consumer<in T> { fun consume(item: T) }inline fun <reified T> printType()Covariance, Contravariance, and Reified Runtime Type Checking
kotlinkotlin
1234567891011121314151617181920212223242526272829303132333435363738// Generics Variance and Reified Type Parameterspackage com.kwasacademy.genericsopen class Shape(val name: String)class Circle(val radius: Double) : Shape("Circle")// 1. Covariant Producer (out T): Can only return T, never accept Tinterface Source<out T> {fun fetch(): T}class CircleSource(private val circle: Circle) : Source<Circle> {override fun fetch(): Circle = circle}// 2. Reified Generic Function: Preserves type info at runtime!inline fun <reified T> findByType(items: List<Any>): List<T> {val results = mutableListOf<T>()for (item in items) {if (item is T) { // 'is T' is legal ONLY because of 'reified'!results.add(item)}}return results}fun main() {// Covariance in action: Source<Circle> can be assigned to Source<Shape>!val circleSource: Source<Circle> = CircleSource(Circle(5.0))val shapeSource: Source<Shape> = circleSource // Valid because of 'out T'!println("Fetched Shape: ${shapeSource.fetch().name}")// Reified types in actionval mixedList = listOf("Hello", 42, Circle(10.0), "KWAS Academy", 99.9)val stringsOnly: List<String> = findByType(mixedList)println("Extracted Strings: $stringsOnly")}
Line-by-Line Technical Breakdown
1PECS Rule (Producer-Extends, Consumer-Super): In Kotlin, if a generic class only *outputs* T, mark it `out T` (Covariant). If it only *consumes* T, mark it `in T` (Contravariant). If it both consumes and outputs T, leave it invariant.
Try It Yourself (Interactive Editor)
Modify the code in real-time and click Run to test live browser output and console logs.
Intelligent Code Runner & Live Sandbox[KOTLIN]
KOTLIN SOURCE EDITOR
Interactive Live CodeCommon Mistakes & How to Avoid Them
#1: Attempting to use `item is T` in a standard non-inlined generic function.
Generic type arguments are erased at compile time on the JVM. Only inlined functions with the 'reified' keyword preserve type metadata at the bytecode level.
Incorrect / Antipattern
fun <T> check(item: Any): Boolean = item is T // Compiler Error: Cannot check for erased typeCorrect / Professional Solution
inline fun <reified T> check(item: Any): Boolean = item is TIndustry Best Practices & Professional Standards
- Use `out` variance for read-only data sources, collections, and event producers.
- Use `in` variance for comparators, serializers, and event handlers.
- Use `inline fun <reified T>` to eliminate boilerplate `Class<T>` parameters in JSON parsers and dependency locators.
Lesson Summary & Core Takeaways
- `out T` declares covariance (producers); `in T` declares contravariance (consumers).
- Declaration-site variance avoids cumbersome Java use-site wildcard annotations.
- `reified` type parameters in inline functions bypass JVM type erasure.