Kotlin Generics

Generics let you write code that works with any type instead of locking it to one specific type. A function or class can accept a type as a parameter — just as a function accepts a value as a parameter. This makes your code reusable and type-safe at the same time.

The Problem Generics Solve

// Without generics — you write the same box for every type:
class IntBox(val value: Int)
class StringBox(val value: String)
class DoubleBox(val value: Double)

// With generics — one class handles all types:
class Box(val value: T)

val intBox    = Box(42)
val stringBox = Box("Kotlin")
val doubleBox = Box(3.14)

println(intBox.value)     // 42
println(stringBox.value)  // Kotlin

Generic Functions

A generic function declares a type parameter before the function name using angle brackets.

fun  printTwice(item: T) {
    println(item)
    println(item)
}

printTwice("Hello")    // Hello \n Hello
printTwice(100)        // 100 \n 100
printTwice(true)       // true \n true

Generic Function That Returns a Value

fun  firstItem(list: List): T {
    return list.first()
}

println(firstItem(listOf("A", "B", "C")))   // A
println(firstItem(listOf(10, 20, 30)))       // 10

Generic Classes

class Pair(val first: A, val second: B) {
    fun swap(): Pair = Pair(second, first)
    override fun toString() = "($first, $second)"
}

val coords = Pair(10, 20)
println(coords)           // (10, 20)
println(coords.swap())    // (20, 10)

val nameAge = Pair("Priya", 25)
println(nameAge)          // (Priya, 25)

Diagram — Generic Type Parameter Flow

Pair       Pair

    A  ←────────── "Priya"   (String)
    B  ←────────── 25        (Int)

first: A  =  "Priya"
second: B  =  25

swap() returns Pair = Pair

Type Constraints

You can restrict which types a generic can accept using an upper bound. The type parameter must be a subtype of the specified class or interface.

fun > findMax(a: T, b: T): T {
    return if (a > b) a else b
}

println(findMax(10, 20))         // 20
println(findMax("Kotlin", "Java"))  // Kotlin (alphabetically)
// findMax(object1, object2)    // ERROR: object1 is not Comparable

Multiple Constraints with where

fun  process(item: T) where T : Comparable, T : Cloneable {
    // T must be both Comparable AND Cloneable
}

Variance — in and out

Variance controls whether a generic type can be used as a supertype or subtype in a type hierarchy.

out (Covariance) — Producer

Use out when the class only produces values of type T — you can read but not write.

class Supplier(private val value: T) {
    fun get(): T = value
}

val intSupplier: Supplier = Supplier(42)
val anySupplier: Supplier = intSupplier   // OK because of 'out'
println(anySupplier.get())   // 42

in (Contravariance) — Consumer

Use in when the class only consumes values of type T — you can write but not read.

class Printer {
    fun print(item: T) { println(item) }
}

val anyPrinter: Printer = Printer()
val stringPrinter: Printer = anyPrinter   // OK because of 'in'
stringPrinter.print("Hello")   // Hello

Diagram — in vs out

out T (covariant) — Producer:
  Supplier can be used as Supplier
  "Specific type → General type" is allowed
  Read-only

in T (contravariant) — Consumer:
  Printer can be used as Printer
  "General type → Specific type" is allowed
  Write-only

Star Projection — * (Unknown Type)

Use * when you want to work with a generic collection but do not care what type it holds.

fun printSize(list: List<*>) {
    println("Size: ${list.size}")
}

printSize(listOf(1, 2, 3))          // Size: 3
printSize(listOf("a", "b"))         // Size: 2
printSize(listOf(true, false, true)) // Size: 3

Reified Type Parameters — Accessing T at Runtime

Generic types are normally erased at runtime (type erasure). The reified keyword — used only with inline functions — preserves the type information so you can use it at runtime.

inline fun  isType(value: Any): Boolean {
    return value is T
}

println(isType("Hello"))   // true
println(isType("Hello"))      // false
println(isType(42))           // true

Leave a Comment

Your email address will not be published. Required fields are marked *