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