Kotlin Generics: Type Parameters, Variance (in/out), Reified Types & Bounds

1. What are generics in Kotlin?

Generics in Kotlin allow classes, functions, and interfaces to operate on types specified as parameters, enabling type-safe, reusable code. They use type parameters (e.g., <T>) to ensure compile-time type checking, preventing runtime errors. Kotlin generics support variance (in/out) and reified types (inline functions), making them more flexible than Java generics.

Benefits of Generics:

How do generics differ from C/C++?

2. How do you define generic classes and interfaces in Kotlin?

Use type parameters in angle brackets (<T>) for classes/interfaces.

Syntax:

class GenericClass<T>(val item: T)
interface GenericInterface<T>
      

Multiple Parameters: <T, U>.

Bounds: T : UpperBound to constrain types.

3. Can you give an example of generic classes and interfaces?

// Generic class
class Box<T>(private val item: T) {
    fun getItem(): T = item
    fun <R> map(transform: (T) -> R): Box<R> = Box(transform(item))
}

// Generic interface
interface Repository<T> {
    fun save(item: T): Boolean
    fun findById(id: Int): T?
}

// Implementation
class StringRepository : Repository<String> {
    private val data = mutableListOf<String>()
    
    override fun save(item: String): Boolean {
        return data.add(item)
    }
    
    override fun findById(id: Int): String? {
        return data.getOrNull(id)
    }
}

// Usage
fun main() {
    val stringBox = Box("Hello")
    println(stringBox.getItem())  // Hello
    
    val intBox = Box(42)
    val doubled = intBox.map { it * 2 }
    println(doubled.getItem())  // 84
    
    val repo = StringRepository()
    repo.save("Kotlin")
    println(repo.findById(0))  // Kotlin
}
      

Output:

Hello
84
Kotlin
      

Note:

4. How do you define generic functions in Kotlin?

Place type parameters before the function name (e.g., fun <T> func(item: T)).

Bounds: <T : Comparable<T>> for constrained types.

Reified Types: Use inline for runtime type access (e.g., inline fun <reified T> func()).

5. Can you give an example of generic functions?

// Generic function
fun <T> findMax(list: List<T>): T where T : Comparable<T> {
    return list.maxOrNull() ?: throw IllegalArgumentException("Empty list")
}

// Reified generic function
inline fun <reified T> isType(obj: Any): Boolean {
    return obj is T
}

// Usage
fun main() {
    val numbers = listOf(5, 3, 8, 1)
    val maxNum = findMax(numbers)
    println("Max number: $maxNum")  // 8
    
    val strings = listOf("apple", "banana", "cherry")
    val maxString = findMax(strings)
    println("Max string: $maxString")  // cherry
    
    println("Is 42 a String? ${isType<String>(42)}")  // false
    println("Is 'hello' a String? ${isType<String>("hello")}")  // true
}
      

Output:

Max number: 8
Max string: cherry
Is 42 a String? false
Is 'hello' a String? true
      

Note:

6. What is variance in Kotlin generics?

Variance: Defines how generics behave with inheritance (e.g., List<out T> for read-only).

Use Case: Safe type handling (e.g., List<out Animal> accepts List<Dog>).

7. Can you give an example of variance?

open class Animal(val name: String)
class Dog(name: String) : Animal(name)
class Cat(name: String) : Animal(name)

fun <T> processAnimals(animals: List<T>) {
    animals.forEach { println(it.name) }
}

// Covariant: List<out Animal> accepts List<Dog>
fun printAnimalNames(animals: List<out Animal>) {
    animals.forEach { println(it.name) }
}

// Contravariant: Function accepting Animal accepts Dog
fun <T> feedAnimal(animal: T) where T : Animal {
    println("Feeding ${animal.name}")
}

// Usage
fun main() {
    val dogs = listOf(Dog("Buddy"), Dog("Max"))
    val cats = listOf(Cat("Whiskers"))
    
    // Covariance: List<Dog> is accepted as List<out Animal>
    printAnimalNames(dogs)
    
    // Contravariance: feedAnimal(Dog) works with Animal parameter
    feedAnimal(Dog("Rex"))
    feedAnimal(Cat("Luna"))
}
      

Output:

Buddy
Max
Feeding Rex
Feeding Luna
      

Note:

8. What are reified generics in Kotlin?

Reified Generics: Allow access to type parameters at runtime (erased in Java/Kotlin generics). Requires inline functions.

Syntax: inline fun <reified T> func().

Use Case: Type checks, serialization, or reflection.

9. Can you give an example of reified generics?

inline fun <reified T> isType(obj: Any): Boolean {
    return obj is T
}

inline fun <reified T> createInstance(): T {
    return T::class.java.newInstance()  // Simplified; use factory for complex types
}

fun main() {
    val num = 42
    val str = "Hello"
    
    // Type checking with reified
    println("42 is Int: ${isType<Int>(num)}")  // true
    println("Hello is String: ${isType<String>(str)}")  // true
    
    // Creating instance (example with data class)
    data class Point(val x: Int, val y: Int)
    
    // For data classes, use factory function
    inline fun <reified T : Point> createPoint(x: Int, y: Int): T {
        return T::class.constructors.first().call(x, y) as T
    }
    
    val point = createPoint<Point>(10, 20)
    println("Point: $point")
}
      

Output:

42 is Int: true
Hello is String: true
Point: Point(x=10, y=20)
      

Note:

10. Can you provide a comprehensive example of generics in Kotlin?

data class Employee(val name: String, val salary: Double)

// Generic class with variance
class Repository<out T>(private val items: List<T>) {
    fun getAll(): List<T> = items
    fun <R> map(transform: (T) -> R): List<R> = items.map(transform)
}

// Generic interface
interface Processor<T> {
    fun process(item: T): String
}

// Generic function with reified
inline fun <reified T> createRepository(vararg items: T): Repository<T> {
    return Repository(listOf(*items))
}

// Covariant usage
fun <T : Comparable<T>> findMax(items: List<T>): T {
    return items.maxOrNull() ?: throw IllegalArgumentException("Empty list")
}

// Usage
fun main() {
    // Generic repository
    val repo = createRepository("Krishna", "Ram", "Charlie")
    println("Repository items: ${repo.getAll()}")
    
    val mapped = repo.map { it.uppercase() }
    println("Mapped: $mapped")
    
    // Find max
    val salaries = listOf(60000.0, 55000.0, 65000.0)
    val maxSalary = findMax(salaries)
    println("Max salary: $maxSalary")
    
    // Reified check
    val list = listOf("Hello", 42, true)
    println("List contains String: ${list.any { it is String }}")
}
      

Output:

Repository items: [Krishna, Ram, Charlie]
Mapped: [Krishna, Ram, CHARLIE]
Max salary: 65000.0
List contains String: true
      

Description:

11. What are common mistakes in Kotlin generics?

Generic Classes/Functions:

Variance:

Reified Generics:

General:

12. What are best practices for Kotlin generics?

Generic Classes/Functions:

Variance:

Reified Generics:

General: