Middle
How to work with generic types (generics) in Kotlin?
sobes.tech AI
Answer from AI
Generic types in Kotlin allow creating classes, interfaces, and functions that work with data of different types, while maintaining type safety at compile time.
Main concepts and usage:
- Declaring generic classes and interfaces:
class Box<T>(val item: T) // Generic class with type parameter T interface MyList<E> { // Generic interface with type parameter E fun add(element: E) fun get(index: Int): E } - Declaring generic functions:
fun <T> printAnything(value: T) { // Generic function with type parameter T println(value) } - Creating instances of generic types:
val intBox: Box<Int> = Box(123) // Specifying a concrete type (Int) val stringBox = Box("hello") // Compiler infers the type (String) - Variance: Defines how subtyping between more complex types relates to subtyping between their component types.
- Covariance (out): Allows using a type and its subtypes. Denoted as
out. Example:List<out Any>can containList<String>. Used for types that produce values (e.g., reading from a collection).interface Producer<out T> { // out T - covariant type parameter fun produce(): T } - Contravariance (in): Allows using a type and its supertypes. Denoted as
in. Example:Comparator<in String>can work withComparator<Any>. Used for types that consume values (e.g., writing to a collection).interface Consumer<in T> { // in T - contravariant type parameter fun consume(value: T) } - Invariance: The type in a generic parameter must be exactly the same. This is the default behavior.
- Covariance (out): Allows using a type and its subtypes. Denoted as
- Type constraints (
where): Allows restricting the type parameter to implement certain interfaces or inherit from a specific class.fun <T : Comparable<T>> max(a: T, b: T): T { // T must implement Comparable return if (a > b) a else b } fun <T> process(item: T) where T : Runnable, T : Comparable<T> { item.run() // ... using comparability } - Star projection (
*): Used when specific type arguments are not important. Similar to<?>in Java.fun printList(list: List<*>) { // Can accept a list of any type for (item in list) { println(item) } }
Generic types in Kotlin use type erasure, like in Java. Information about specific type parameters is lost at runtime. To work with types at runtime, reified types are used.
- Reified types (
reified): Available only for inline functions. Allow preserving type information at runtime.inline fun <reified T> checkType(value: Any) { // T is a reified type if (value is T) { println("Value is of type T") } else { println("Value is not of type T") } }