Search code examples
kotlinkotlin-extensionkotlin-generics

Can I extend Comparable<T> in Kotlin?


I wanted to implement min()/max() aliases for the Kotlin's Comparable<T>coerceAtLeast()/coerceAtMost(), if nothing else for the exercise of extending an Interface (I've only extended classes so far).

I tried this:

fun <T>Comparable<T>.max(other:T) : T {
    return this.coerceAtLeast(other)
}

But I get the following error:

Type inference failed: Cannot infer type parameter T in fun <T : Comparable<T#1 (type parameter of kotlin.ranges.coerceAtLeast)>> T#1.coerceAtLeast(minimumValue: T#1): T#1
None of the following substitutions
receiver: Any?  arguments: (Any?)
receiver: Comparable<T#2 (type parameter of com.nelsonirrigation.twig.plans.extensions.max)>  arguments: (Comparable<T#2>)
receiver: T#2  arguments: (T#2)
receiver: Comparable<Comparable<T#2>>  arguments: (Comparable<Comparable<T#2>>)
can be applied to
receiver: Comparable<T#2>  arguments: (T#2)

At which point my limited understanding of Kotlin generics basically overflowed. Is what I'm trying to do achievable? What is the piece of the puzzle I'm missing?


Solution

  • You'll notice in the implementation of coerceAtLeast that the declaration of the extension function is a little different from what you have:

    fun <T : Comparable<T>> T.coerceAtLeast(minimumValue: T): T
    

    If you change your declaration to match, it compiles.

    This comes down to the problem of the type of minimumValue. In your version, it's not enforced that minimumValue's type implements Comparable<T>, so it can't coerce your other to a type that complies with the contract of coerceAtLeast.

    Note that it is possible to write an extension function on an interface directly, it's a consequence of calling another method that doesn't match your typing configuration that causes this to break.