Type Constraints

Upper Bounds

A constraint limits which types can be used. <T : Number> means "T must be Number or a subtype", so you can call Number's functions on T. The default upper bound is Any?.

Example

fun <T : Number> average(values: List<T>): Double = values.sumOf { it.toDouble() } / values.size

fun <T : Comparable<T>> maxOf3(a: T, b: T, c: T): T = maxOf(a, maxOf(b, c))

fun main() {
    println(average(listOf(10, 20, 30)))
    println(average(listOf(1.5, 2.5)))
    println(maxOf3("apple", "mango", "banana"))
    println(maxOf3(3, 9, 4))
    // average(listOf("a"))  -> error: String is not a Number
}

Output

20.0
2.0
mango
9

Multiple Constraints with where

Example

interface Identifiable { val id: Int }
interface Named { val name: String }

data class Customer(override val id: Int, override val name: String) : Identifiable, Named

fun <T> label(items: List<T>): List<String> where T : Identifiable, T : Named =
    items.sortedBy { it.id }.map { "#${it.id} ${it.name}" }

fun main() {
    println(label(listOf(Customer(3, "Ravi"), Customer(1, "Asha"))))
}

Output

[#1 Asha, #3 Ravi]