Kotlin Generics Deep Dive: Variance, Reified & Type Constraints
Kotlin's generic system is more powerful than Java's. This guide covers variance, reified type parameters, and practical type constraints.
Understanding Variance
Kotlin has covariance (out) and contravariance (in):
// Covariance: Producer of T
interface Producer<out T> {
fun produce(): T
}
// Contravariance: Consumer of T
interface Consumer<in T> {
fun consume(item: T)
}
// Invariance: Both producer and consumer (default)
interface Storage<T> {
fun store(item: T)
fun retrieve(): T
}
Covariance allows safer type substitution:
val stringProducer: Producer<String> = object : Producer<String> {
override fun produce() = "Hello"
}
val anyProducer: Producer<Any> = stringProducer // Valid due to covariance
Reified Type Parameters
Reified parameters preserve type information at runtime:
inline fun <reified T> parseJson(json: String): T {
return Gson().fromJson(json, T::class.java)
}
data class User(val name: String, val age: Int)
val user = parseJson<User>(jsonString)
Without reified, type information is erased:
// This would NOT work
fun <T> parseJsonBad(json: String): T {
// Cannot access T::class here - type is erased
return TODO()
}
Type Constraints & Upper Bounds
Constrain type parameters with upper bounds:
// T must be Comparable
fun <T : Comparable<T>> findMax(items: List<T>): T? {
return items.maxOrNull()
}
// Multiple bounds
fun <T> process(item: T) where T : Serializable, T : Comparable<T> {
// T must implement both Serializable and Comparable
}
Practical Pattern: Generic Extension
fun <T : Any> Any?.requireOfType(): T {
require(this is T) {
"Expected ${T::class.simpleName}, got ${this?.javaClass?.simpleName}"
}
return this as T
}
val obj: Any = "Hello"
val str: String = obj.requireOfType() // Safe cast with validation
Type Erasure & Solutions
Despite some erasure, Kotlin provides workarounds:
// Check instance with erased type (works for reified only)
inline fun <reified T> isInstance(obj: Any) = obj is T
// Store type information explicitly
class TypeToken<T>
val listType = object : TypeToken<List<String>>() {}
8 Android app templates on Gumroad
Top comments (0)