1

Given a method which accepts a parameter of a generic type, I would like to check if the type implements Comparable, and if so, compare it with another value.

Assuming the type parameter is T, since T is erased, a test of the form t is Comparable<T> is not possible, even with reified type parameters. A test of the form t is Comparable<*> is possible and works, but then t cannot be used as Comparable, because Comparable<*> is out-projected.

fun <T> examine(a: T, b: T) {
    if (a is Comparable<*>) {
        a <= b
    }
}

This fails with:

Kotlin: Out-projected type 'Comparable<*>' prohibits the use of 'public abstract operator fun compareTo(other: T): Int defined in kotlin.Comparable'

The one workaround that I can see is

fun <T> examine(a: T, b: T) {
    if (a is Comparable<*>) {
        println(a as Comparable<T> <= b)
    }
}

which has the unchecked cast, but why is this cast necessary?

Should smart casting not take care of this because of the test we do to check if a is comparable? Are the different type parameters not allowing the smart cast to happen here?

Is this the recommended way to test for Comparable or is there another solution which could avoid unchecked casts?

abhijat
  • 164
  • 4
  • 8

2 Answers2

3

The cast in your example is obviously necessary because you only ensure that a is a Comparable of an unknown type (star projected). And this cast can even fail. Think about a class A: Comparable<B>. You would cast it to Comparable<A> although it can only be compared to B.

You can also think about providing two functions, one restricted to T: Comparable<T> using an upper bound and one for non-comparable types. In this case, you would not need to cast anything.

s1m0nw1
  • 56,594
  • 11
  • 126
  • 171
  • https://stackoverflow.com/questions/36253310/how-to-get-actual-type-arguments-of-a-reified-generic-parameter-in-kotlin might also be helpful – s1m0nw1 Mar 25 '19 at 08:46
1

You can't.

This is an unfortunate consequence of type erasure. When generics were first added to Java, for backward compatibility it was done only at compile-time; the compiled code has no knowledge of type parameters or their restrictions. So at run-time there's no such thing as a Compatable<T>, just Comparable. That's why the type parameter can't be checked with is.

Kotlin has a workaround in some circumstances; if the function is inline, you can mark a type parameter as reified, and it can then use the type parameter in the inlined code. But that's not a solution here, as it can only provide the type T, not the parameterised type of Comparable if a implements it.

gidds
  • 9,862
  • 1
  • 12
  • 16
  • Unfortunately, even with reified parameters while T is now visible to the inlined function, it appears that something like `Comparable` is still erased. – abhijat Mar 25 '19 at 08:40