Kotlin – Generics: Write Reusable and Type-Safe Code
Introduction – Why Learn Kotlin Generics?
Generics in Kotlin let you write flexible and type-safe code without sacrificing readability. Instead of creating separate classes or functions for every data type, generics let you define a blueprint that works with any type. This is widely used in collections, APIs, utility classes, and custom data structures.
In this guide, you’ll learn:
- What generics are and why they matter
- How to define and use generic functions and classes
- The role of variance (
in,out) and type constraints - Best practices and real-world use cases
What Are Generics in Kotlin?
Generics allow you to write type-agnostic code. You define a placeholder (like T) instead of a specific type.
Without Generics:
fun printIntList(list: List<Int>) { ... }
With Generics:
fun <T> printList(list: List<T>) {
for (item in list) println(item)
}
✔️ Now you can pass a list of any type (Int, String, Double, etc.).
Generic Classes – Define Once, Use for All Types
class Box<T>(val value: T) {
fun getValue(): T = value
}
Usage:
val intBox = Box(100)
val strBox = Box("Kotlin")
println(intBox.getValue()) // Output: 100
println(strBox.getValue()) // Output: Kotlin
✔️ Box<T> can wrap any type!
Generic Functions
fun <T> display(item: T) {
println("Item: $item")
}
Usage:
display("Hello")
display(42)
✔️ Define once, use for all types.
Variance in Kotlin Generics
Kotlin uses variance modifiers to control how subtyping works with generics.
out – Covariance (Producer)
class Source<out T>(val data: T)
fun read(source: Source<Any>) { ... }
val stringSource: Source<String> = Source("Text")
read(stringSource) // Works because of `out`
✔️ Use out when a type only produces data (read-only).
in – Contravariance (Consumer)
class Sink<in T> {
fun accept(item: T) { println("Accepted: $item") }
}
val sink: Sink<Number> = Sink<Int>() // OK because of `in`
✔️ Use in when a type only consumes data (write-only).
Type Constraints with where or :
Restrict generics to specific types using constraints:
fun <T : Number> doubleValue(value: T): Double {
return value.toDouble() * 2
}
✔️ T must be a subtype of Number.
Multiple Type Parameters
class Pair<A, B>(val first: A, val second: B)
val pair = Pair("Age", 30)
println("${pair.first} = ${pair.second}") // Age = 30
✔️ You can define multiple placeholders for multi-type data structures.
Generic Extension Functions
fun <T> List<T>.second(): T = this[1]
val nums = listOf(10, 20, 30)
println(nums.second()) // Output: 20
✔️ Extend built-in types generically!
Common Mistakes
| Mistake | Fix |
|---|---|
Forgetting <T> in function header | Always declare generic before return type |
Misusing in/out | Use out for producing, in for consuming |
| Ignoring constraints | Use : Type to restrict generic usage |
Using star-projection (*) too much | Use variance properly instead of fallback wildcards |
Best Practices for Kotlin Generics
| Practice | Why It Matters |
|---|---|
| Keep generics simple and readable | Avoid overly abstract or complex type setups |
| Use variance modifiers where applicable | Prevent unsafe type casting and enhance safety |
| Add type constraints to clarify usage | Makes the contract of your function/class obvious |
| Combine generics with inline and reified | Enables smart casting and reflection for T |
Summary – Recap & Next Steps
Kotlin Generics let you build type-safe, reusable, and maintainable code across classes, functions, and APIs. Mastering variance, constraints, and usage patterns helps you design flexible systems.
Key Takeaways:
- Use
<T>to define generics in classes and functions - Use
inandoutfor type-safe inheritance scenarios - Apply constraints with
: Typeorwhereclauses - Keep generic logic simple and predictable
Practical Use:
Used heavily in collections, custom data containers, repository patterns, utility libraries, and API response models in Kotlin apps.
FAQs – Kotlin Generics
What is a generic in Kotlin?
A generic is a type placeholder (like T) that allows classes and functions to operate on different types without rewriting them.
What is out in Kotlin generics?
It’s a covariance modifier meaning the class is read-only and can produce values of type T.
How do I restrict a generic to certain types?
Use type constraints:
fun <T : Number> process(value: T) { ... }
Can I use generics in extension functions?
Yes. You can write powerful and reusable generic extensions:
fun <T> List<T>.lastIndex(): Int = this.size - 1
What’s the difference between in and out?
in is for consuming types, out is for producing types. Use in when you write to a generic type, and out when you read from it.
Share Now :
