🧬 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 outfor producing,infor consuming | 
| Ignoring constraints | Use : Typeto 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 :
