kotlinadvanced
Generics — Variance, Bounds, and Type Projections
Master Kotlin generics: in/out variance, upper bounds, type projections, and generic constraints.
kotlinPress ⌘/Ctrl + Shift + C to copy
// Covariance (out) — producer, read-only
interface Source<out T> {
fun next(): T
}
// Contravariance (in) — consumer, write-only
interface Sink<in T> {
fun put(item: T)
}
// Invariant — both reading and writing
interface Container<T> {
fun get(): T
fun set(item: T)
}
// Covariant list — safe to use List<Dog> as List<Animal>
open class Animal(val name: String)
class Dog(name: String, val breed: String) : Animal(name)
class Cat(name: String) : Animal(name)
fun printAnimals(animals: List<Animal>) {
animals.forEach { println(it.name) }
}
// Upper bound constraint
fun <T : Comparable<T>> maxOf(a: T, b: T): T = if (a >= b) a else b
// Multiple bounds (where clause)
fun <T> copyIfValid(item: T): String where T : Comparable<T>, T : CharSequence {
return if (item.length > 0) item.toString() else "empty"
}
// Generic class with bound
class SortedList<T : Comparable<T>> {
private val items = mutableListOf<T>()
fun add(item: T) {
items.add(item)
items.sort()
}
fun getAll(): List<T> = items.toList()
override fun toString() = items.toString()
}
// Star projection
fun printAll(items: List<*>) {
items.forEach { println(it) }
}
// Type-safe heterogeneous container
class TypeMap {
private val map = mutableMapOf<Class<*>, Any>()
fun <T : Any> put(type: Class<T>, value: T) {
map[type] = value
}
@Suppress("UNCHECKED_CAST")
fun <T : Any> get(type: Class<T>): T? = map[type] as? T
inline fun <reified T : Any> put(value: T) = put(T::class.java, value)
inline fun <reified T : Any> get(): T? = get(T::class.java)
}
// Generic extension
fun <T> T.toSingletonList(): List<T> = listOf(this)
fun <T : Comparable<T>> T.coerceIn(range: ClosedRange<T>): T = when {
this < range.start -> range.start
this > range.endInclusive -> range.endInclusive
else -> this
}
fun main() {
// Covariance — List is covariant (out)
val dogs: List<Dog> = listOf(Dog("Rex", "Lab"), Dog("Spot", "Beagle"))
printAnimals(dogs) // Works! List<Dog> is subtype of List<Animal>
// Upper bound
println(maxOf(3, 7)) // 7
println(maxOf("abc", "xyz")) // xyz
// SortedList
val sorted = SortedList<Int>()
sorted.add(5); sorted.add(1); sorted.add(3)
println("Sorted: $sorted") // [1, 3, 5]
// TypeMap
val config = TypeMap()
config.put("Hello")
config.put(42)
config.put(3.14)
println("String: ${config.get<String>()}")
println("Int: ${config.get<Int>()}")
println("Double: ${config.get<Double>()}")
// Extensions
println(42.toSingletonList()) // [42]
println(150.coerceIn(0..100)) // 100
}Use Cases
- Type-safe generic APIs and containers
- Variance declarations for library design
- Bounded type constraints for safety
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
kotlinadvanced
Kotlin Generics Variance In Out Star
Understand Kotlin generics: declaration-site variance with in/out, type projections, and star projection.
Best for: Type-safe container hierarchies
#kotlin#generics
kotlinintermediate
Sealed Classes and Exhaustive when
Model restricted hierarchies with sealed classes: exhaustive when, data objects, and state machines.
Best for: Type-safe API response handling
#kotlin#sealed-class
kotlinadvanced
Inline Functions and Reified Generics
Use inline functions for zero-overhead abstractions: reified type parameters, crossinline, and noinline.
Best for: Type-safe parsing and casting utilities
#kotlin#inline
kotlinintermediate
Value Classes — Zero-Cost Wrappers
Create type-safe wrappers with value classes: no runtime overhead, domain identifiers, and units.
Best for: Type-safe domain identifiers
#kotlin#value-class