Kotlin Generics

Type-safe programming with flexible, reusable code

🔧 What are Kotlin Generics?

Generics allow you to write flexible, reusable code that works with different types while maintaining type safety. They help create classes and functions that can handle various data types without losing compile-time type checking.


// Generic function example
fun <T> printItem(item: T) {
    println("Item: $item")
}

printItem("Hello")  // Works with String
printItem(42)       // Works with Int
                                    

Output:

Item: Hello

Item: 42

Key Generic Concepts

📦

Generic Classes

Classes that work with any type

class Box<T>(val item: T)
âš¡

Generic Functions

Functions that accept any type

fun <T> swap(a: T, b: T): Pair<T, T>
🔒

Type Constraints

Limit generic types to specific bounds

fun <T : Number> sum(a: T, b: T)
🔄

Variance

Control type relationships with in/out

interface Producer<out T>

🔹 Generic Classes

Create classes that can work with different types:

// Generic class definition
class Container<T>(private var item: T) {
    fun get(): T = item
    fun set(newItem: T) {
        item = newItem
    }
}

// Usage
val stringContainer = Container("Hello")
val intContainer = Container(123)

println(stringContainer.get()) // Hello
println(intContainer.get())     // 123

Output:

Hello

123

🔹 Generic Functions

Functions that work with multiple types:

// Generic function with type parameter
fun <T> findFirst(list: List<T>, predicate: (T) -> Boolean): T? {
    for (item in list) {
        if (predicate(item)) return item
    }
    return null
}

// Usage examples
val numbers = listOf(1, 2, 3, 4, 5)
val names = listOf("Alice", "Bob", "Charlie")

val firstEven = findFirst(numbers) { it % 2 == 0 }
val longName = findFirst(names) { it.length > 4 }

println("First even: $firstEven")     // 2
println("Long name: $longName")       // Alice

Output:

First even: 2

Long name: Alice

🔹 Type Constraints

Restrict generic types to specific bounds:

// Upper bound constraint
fun <T : Number> addNumbers(a: T, b: T): Double {
    return a.toDouble() + b.toDouble()
}

// Multiple constraints
fun <T> processComparable(item: T): String 
    where T : Comparable<T>, T : CharSequence {
    return "Item '$item' has length ${item.length}"
}

// Usage
val sum = addNumbers(10, 20.5)
val result = processComparable("Hello")

println("Sum: $sum")           // 30.5
println(result)                // Item 'Hello' has length 5

Output:

Sum: 30.5

Item 'Hello' has length 5

🧠 Test Your Knowledge

What symbol is used to define a generic type parameter?