6

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.

activedecay
  • 10,129
  • 5
  • 47
  • 71
Jeremy Lyman
  • 3,084
  • 1
  • 27
  • 35

2 Answers2

16

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

Jeremy Lyman
  • 3,084
  • 1
  • 27
  • 35
  • 4
    It isn't exactly "generic type erasure". Kotlin can differentiate at compile time between the two functions because its signatures include the generics, and can also differentiated by return type. Java does not create method signatures with generic types. So this changes the actual method name in the byte code renaming it so that there are now two independent methods with no more need to differentiate by signature. Rather than two overloaded functions on a class with apparently the same signature. – Jayson Minard Jan 11 '16 at 12:28
0

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.

activedecay
  • 10,129
  • 5
  • 47
  • 71
  • This example uses obsolete Kotlin syntax, which is no longer compatible with the current version. Jeremy's answer above shows the same solution using up-to-date syntax. – yole Jan 10 '16 at 18:13
  • `platformName` is deprecated (or now removed), `JvmName` is current. Update to Kotlin 1.0 betas and watch your compiler warnings/errors. – Jayson Minard Jan 11 '16 at 12:24