Type constraints in Kotlin are powerful features that allow developers to specify requirements for type parameters in generic classes and functions. They enhance type safety and enable more precise control over generic code.
Type constraints, also known as bounds, limit the types that can be used as type arguments in a generic context. They ensure that only types meeting specific criteria are accepted, providing additional compile-time safety and enabling access to specific members of the constrained types.
In Kotlin, type constraints are defined using the :
symbol followed by the upper bound. Here's the basic syntax:
fun <T : UpperBound> someFunction(param: T) {
// Function body
}
In this example, T
is constrained to be a subtype of UpperBound
.
fun <T : Number> square(value: T): Double {
return value.toDouble() * value.toDouble()
}
val result = square(5) // Works with Int
val result2 = square(3.14) // Works with Double
// val result3 = square("3") // Compile error: String is not a subtype of Number
Kotlin allows specifying multiple upper bounds using the where
clause:
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
where T : CharSequence,
T : Comparable<T> {
return list.filter { it > threshold }.map { it.toString() }
}
val result = copyWhenGreater(listOf("apple", "banana", "cherry"), "b")
println(result) // [cherry]
While type constraints are powerful, they can sometimes lead to complex type hierarchies. It's essential to balance type safety with code readability and maintainability. In some cases, using Kotlin Inline Functions with reified type parameters can provide an alternative to complex type constraints.
Type constraints in Kotlin offer a robust way to create more specific and type-safe generic code. By understanding and applying them effectively, developers can write more reliable and self-documenting generic functions and classes.