1. Overview
In this article, we will learn the constraint type parameter Kotlin.
2. Generic Constraints
You can use generic constraints to restrict the type parameter T to a set of all expected types. Kotlin supports the upper bounds constraint as of v1.5.30 which is discussed below and it’s possible that Kotlin might support other constraint types in the future.
2.1. Upper bound constraint.
Let’s take the below example to understand the upper bound constraint. We want a function to take a generic element as input and then return a list by adding that element.
Here, the listOfSingleElement
function does that. If the element is null, then it returns an empty list.
fun <T : BaseClass> listOfSingleElement(element: T?): List<T> { return if (element != null) listOf(element) else emptyList() }
The specified type BaseClass
after the semicolon is the upper bound. Why we need that? It restricts T to accept only subtypes of BaseClass
.
For example, the below code will throw a compile-time error because we are passing OtherClass
that is not sub type of BaseClass
.
fun main() { val list = listOfSingleElement(OtherClass()) println("Size of list : " + list.size) } open class BaseClass() { private val name = "BaseClass"; } class SubClass : BaseClass() { private val name = "SubClass"; } class OtherClass { }
Result:
Type mismatch: inferred type is SubClass but BaseClass was expected.
But, if you pass SubClass
, it will work without any error as it is sub type of BaseClass
.
fun main() { val list = listOfSingleElement(SubClass()) println("Size of list : " + list.size) }
Result:
Size of list : 1
Suppose you want T to accept a class that implements CharSequence
and Comparable
. But you can specify only one constraint on the angle brackets <T> before the name of the function. You can’t specify multiple constraints before the function name.
For such a scenario, you had to use where clause which is discussed in the next section.
2.1.2. Multiple Constraints on one parameter.
As mentioned earlier, you can use where clause to impose multiple constraints on the same type parameter. This multiple type bounds are connected by “and”, not “or”, so the T type will satisfy all conditions of 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() } }
In the above example, the T
type will accept only the types that implement both CharSequence
and Comparable
.
2.1.2. Default constraint (Any?)
If you don’t specify any constraint, then the default constraint is Any?
so that T accepts any type. As we already know, Any? is the super type of all types. There is no need to specify Any? explicitly.
fun <T> listOfSingleElement(element: T?): List<T> { return if (element != null) listOf(element) else emptyList() }
The above <T> before the name of the function infers to the <T : Any?> as shown in the below example.
fun <T : Any?> listOfSingleElement(element: T?): List<T> { return if (element != null) listOf(element) else emptyList() }
Suppose you want the T type to accept only non-null values, then you should specify Any explicitly. In the below example, we have Any as constraint <T: Any> so that T accepts only non-null values.
fun <T : Any> listOfSingleElement(element: T): List<T> { return listOf(element) }
2.1.3. Multiple Type Parameters.
Suppose your function has multiple generic parameters that accepts different types, then you had to specify constraints separated by comma.
Let’s see an example to understand this concept. The below function takes two generic parameters T and K where T accepts only sub type of BaseClass
whereas K accepts sub types of ValidationClass
.
fun <T: BaseClass, K: ValidationClass> listOfSingleElement(element: T, validation: K): List<T> { // code }
2.1.4. Two classes as upper bounds for same parameter.
First, this is not supported in Kotlin. Let’s see why.
Kotlin doesn’t support multiple inheritance so same class can’t be a sub type of more than one class. The below multiple inheritance will not work with Kotlin.
class A { } class B { } class C : A(), B() { }
So, the same way we can’t restrict T parameter to be a subtype of more than one class. T
would need to be a subtype of both ClassA
and ClassB
, which is of course impossible. But, we can restrict T parameter to class and interface combination.
private fun <T> tryThis(ourClass: T): String where T : ClassA, T: ClassB { // your code }
3. Conclusion
In this article, we learned about the constraint type parameter Kotlin. You can also refer to the Generic constraints official documentation.
If you like to learn more Kotlin related topics, we recommend you to refer our Kotlin articles.