Generics
Generics let you write code that works for many types while keeping full type
safety — no casting, no Any.
Generic functions
A type parameter in angle brackets stands in for a real type chosen at the call site:
fun <T> firstOrNull(items: List<T>): T? =
if (items.isEmpty()) null else items[0]
firstOrNull(listOf("a", "b")) // T = String
firstOrNull(listOf(1, 2)) // T = Int
Generic classes
class Box<T>(val value: T) {
fun <R> map(transform: (T) -> R): Box<R> = Box(transform(value))
}
val boxed = Box(21).map { it * 2 } // Box<Int> holding 42
Constraints (upper bounds)
Restrict a type parameter so you can call certain members:
fun <T : Comparable<T>> largest(items: List<T>): T? =
items.maxOrNull() // needs Comparable to compare
largest(listOf(3, 1, 2)) // 3
largest(listOf("b", "a")) // "b"
For multiple bounds, use a where clause:
fun <T> sortedCopy(items: List<T>): List<T>
where T : Comparable<T>, T : Any = items.sorted()
Nullability and generics
A bare type parameter T may be nullable. Add the : Any bound to forbid null:
fun <T : Any> requireAll(items: List<T?>): List<T> = items.filterNotNull()
Exercises
-
Write a generic function
pairUp<T>(a: T, b: T): List<T>returning a list of the two arguments. -
Write
largest<T : Comparable<T>>(items: List<T>): T?returning the maximum, ornullfor an empty list.Solution
fun <T : Comparable<T>> largest(items: List<T>): T? = items.maxOrNull()
</details>
This solution is in examples/core/part4/ and is tested by CI.
Previous: Sequences · Next: Variance & Reified Types