QUICK START:HTMLCSSJavaScriptTypeScriptPythonSQLReactNext.jsNode.jsLinux & UbuntuKotlinSwiftC# / .NETJavaGoRustC++DSASystem DesignDevOpsCybersecurityAI / ML
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

kotlin
kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Generics Variance and Reified Type Parameters
package com.kwasacademy.generics
open class Shape(val name: String)
class Circle(val radius: Double) : Shape("Circle")
// 1. Covariant Producer (out T): Can only return T, never accept T
interface 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 action
val 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 Code

Common 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 type
Correct / Professional Solution
inline fun <reified T> check(item: Any): Boolean = item is T

Industry 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.