Generics — Basics
| Since: | Kotlin 1.0(2016) |
|---|
Kotlin generics use type parameters to define type-safe collections and algorithms. By adding a type parameter <T> to a function or class, you can write code that works with a variety of types.
Syntax
fun <T> functionName(arg: T): T { return arg }
// Generic class
class Box<T>(val value: T)
// Type constraint (T must implement Comparable)
fun <T : Comparable<T>> max(a: T, b: T): T = if (a > b) a else b
// Multiple type parameters
fun <K, V> pair(key: K, value: V): Pair<K, V> = Pair(key, value)
Syntax Reference
| Syntax | Description |
|---|---|
| <T> | Declares a type parameter. Accepts any type. |
| <T : UpperBound> | Type constraint. T must be a subtype of the upper bound. |
| where T : Type1, T : Type2 | Specifies multiple type constraints. |
| Box<Int> | Uses a generic with a specific type argument. |
| Box<*> | Star projection. Used when the type is unknown (equivalent to Java's Box<?>). |
Sample Code
generics_basic_kotlin.kt
fun <T> firstOrNull(list: List<T>): T? = list.firstOrNull()
fun <T : Comparable<T>> maxOf(a: T, b: T): T = if (a > b) a else b
// Generic class
class Stack<T> {
private val items = mutableListOf<T>()
fun push(item: T) { items.add(item) }
fun pop(): T? = if (items.isEmpty()) null else items.removeAt(items.lastIndex)
fun peek(): T? = items.lastOrNull()
val size: Int get() = items.size
}
// Multiple constraints using a where clause
fun <T> printIfComparableAndSerializable(value: T)
where T : Comparable<T>, T : java.io.Serializable {
println(value)
}
fun main() {
println(firstOrNull(listOf(1, 2, 3))) // 1
println(firstOrNull(emptyList<String>())) // null
println(maxOf(3, 7)) // 7
println(maxOf("Kogami Shinya", "Tsunemori Akane")) // Tsunemori Akane
val stack = Stack<String>()
stack.push("a")
stack.push("b")
stack.push("c")
println(stack.pop()) // c
println(stack.peek()) // b
println(stack.size) // 2
printIfComparableAndSerializable(42) // 42
printIfComparableAndSerializable("hello") // hello
}
The command looks like this:
kotlinc generics_basic_kotlin.kt -include-runtime -d generics_basic_kotlin.jar java -jar generics_basic_kotlin.jar 1 null 7 Tsunemori Akane c b 2 42 hello
Common Mistakes
Common mistake 1: Using a comparison operator in a generic function without a Comparable constraint causes a compile error.
NG example 1 — using > without a Comparable constraint
fun <T> maxOf(a: T, b: T): T {
// return if (a > b) a else b // compile error: T does not implement Comparable
return a
}
generics_basic_kotlin_ok1.kt
fun <T : Comparable<T>> maxOf(a: T, b: T): T = if (a > b) a else b
fun main() {
println(maxOf("Kogami Shinya", "Tsunemori Akane")) // Tsunemori Akane
println(maxOf(3, 7)) // 7
}
The command looks like this:
kotlinc generics_basic_kotlin_ok1.kt -include-runtime -d generics_basic_kotlin_ok1.jar java -jar generics_basic_kotlin_ok1.jar Tsunemori Akane 7
Common mistake 2: Container<String> and Container<Int> are distinct types and cannot be assigned to the same variable.
NG example 2 — treating generics with different type arguments as the same type
class Container<T>(val value: T)
fun main() {
val c1: Container<String> = Container("Kogami Shinya")
// val c2: Container<String> = Container(100) // compile error: Int is not String
}
Generics are invariant by default. Use variance annotations (out/in) to allow assignment compatibility.
Common mistake 3: The default upper bound is Any?, so null can be passed to an unconstrained type parameter.
NG example 3 — null slips through an unconstrained type parameter
fun <T> printValue(v: T) { println(v) }
fun main() {
printValue<String?>(null) // null is accepted
}
generics_basic_kotlin_ok3.kt
fun <T : Any> printNonNull(v: T) { println(v) }
fun main() {
printNonNull("Tsunemori Akane")
// printNonNull(null) // compile error
}
The command looks like this:
kotlinc generics_basic_kotlin_ok3.kt -include-runtime -d generics_basic_kotlin_ok3.jar java -jar generics_basic_kotlin_ok3.jar Tsunemori Akane
Notes
Generics let you write type-safe, reusable functions and classes without duplicating code for each type. Kotlin's type inference means you can often omit the type argument entirely.
Type parameters have Any? as the default upper bound, so they are nullable by default. Writing <T : Any> restricts the parameter to non-null types only.
For variance (covariance and contravariance), see Covariant / Contravariant. For retaining type information at runtime, see Reified Type Parameters.
If you find any errors or copyright issues, please contact us.