Generics — Basics
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
// Generic function
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
// Generic function — returns the first element of any list, or null if empty.
fun <T> firstOrNull(list: List<T>): T? = list.firstOrNull()
// With type constraint — returns the larger of two Comparable values.
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("apple", "mango")) // mango
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
}
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.