4
votes

What is the idiomatic way to add a function to a class based on type. The following example uses List as the class, and the Type Parameter <T> is the class of objects inside of the list. Lets say you want to sort each of these lists with a different comparator based on their type.

data class A(val foo: String)
data class B(val bar: Int)

private val aComparator: Comparator<A> = Comparator { lhs, rhs -> rhs.foo.compareTo(lhs.foo) }
private val bComparator: Comparator<B> = Comparator { lhs, rhs -> rhs.bar.compareTo(lhs.bar) }

fun <T: A> List<T>.sort(): List<T> {
    return this.sortedWith<T>(aComparator)
}

fun <T: B> List<T>.sort(): List<T> {
    return this.sortedWith<T>(bComparator)
}

This gives an error saying that both sort functions have the same signatures due to Java's overloading rules. In Java I might give them both different identifiable names but it is fairly ugly as Kotlin extensions (e.g. a.sortA() b.sortB()). Kotlin doesn't show sortA to a List< B > so there seems like there would be a better way of writing sort() to handle different comparators on different objects.

The example is pretty simple but imagine if I don't have access to modify class A and B so I could not use inheritance or implement an interface on them. I also thought about adding a comparator to every class and use Any? But that seems cumbersome as well.

2

2 Answers

12
votes

One answer seems to be :

@JvmName("sortA")
fun <T: A> List<T>.sort(): List<T> {
    return this.sortedWith<T>(aComparator)
}

@JvmName("sortB")
fun <T: B> List<T>.sort(): List<T> {
    return this.sortedWith<T>(bComparator)
}

This seems to fix Java's issue with generic type erasure.

Found here : https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html

0
votes

On this site, I found this solution

Instead of this

fun Iterable<Long>.average(): Double {}
fun Iterable<Int>.average(): Double {}

Use the platformName

fun Iterable<Long>.average(): Long {
}
platformName("averageOfInt") fun Iterable<Int>.average(): Int {
}

EDIT: This is deprecated, use JvmName instead.