Kotlin type erasure - why are functions differing only in generic type compilable while those only differing in return type are not?


While working on the answer of How does erasure work in Kotlin? I found out some things I did not yet understand, nor did I find any sources why it is that way.

Why is the following not compilable?

fun bar(foo: List<*>) = ""
fun bar(foo: List<*>) = 2

while the following is?

fun bar(foo: List<String>) = ""
fun bar(foo: List<Int>) = 2

For me it even gets more curious, when adding a generic type that isn’t even used, i.e. the following compiles too:

fun bar(foo: List<*>) = ""
fun <T> bar(foo: List<*>) = 2 // T isn't even used

As the last one doesn’t even use T and as we know, generics are erased at runtime, why does this one work, while the variant without generic type does not?

Within the byte code methods only differing in return type are allowed (already described in the above linked answer).

Any hints, sources and/or references are welcome.

Not so sure whether this is a deliberate language design choice or a bug… feel free to move the topic.

This question has also been asked here: https://stackoverflow.com/q/52092214/6202869


While I don’t have an answer, I have a follow up question. Is there a way in Kotlin to invoke both of those functions? Or will always the same one be invoked? Or is there a compile-error when trying to invoke them?


Actually there is now an answer from @yole at stackoverflow and yes. It is possible to call all those functions that are actually compilable :wink:

val x = bar(listOf("") // x = ""
val y = bar(listOf(1)) // y = 2

and it even makes sense now regarding the other (<T>) signature:

val x = bar(listOf(null)) // x = ""
val y = bar<Any>(listOf(null)) // specifying T will call the other, so y = 2


If the JVM includes the return type in signature, it makes sense. However, why is this not allowed in Java, then? :slight_smile:


See: https://stackoverflow.com/questions/34613075/does-java-virtual-machine-allow-overloading-on-return-type

When calling from Java, sometimes it can match the type param and you don’t get ambiguity issues. But sometimes it can’t (i.e. your unused type param example) and you will get an error and have to use reflection. The thing to remember, in Kotlin at least, is what the JVM signature is which is erased param types + return type and that must be unique for a common name.