This following declaration is legal in Kotlin.
fun foo(): String = "foo_1"
fun <T> foo(): T = "foo_2" as T
As bytecode we are getting:
public final static foo()Ljava/lang/String;
// signature <T:Ljava/lang/Object;>()TT;
// declaration: T foo<T>()
public final static foo()Ljava/lang/Object;
It's also possible to call both of these methods from Kotlin.
The problem comes when I'm trying to call any of them from Java:
ClassKt.foo()
Ambiguous call. Both methods match ...
How to avoid such a problem? How to deal with such methods? What if 3-rd party kt library has same issue?
The example above is a synthetic one.
Why does it work with Kotlin to begin with... In Java having two methods like:
private static String test() {
return "";
}
private static <T> T test() {
return null;
}
would result in a compile time error. And for java devs this is sort of obvious, these methods would have the same type erasure. But this is rule imposed by javac
, not by the JVM
where this code runs. So javac
does not treat two methods as having only a different return type as overloads. Well, kotlin
is a different language and since it runs on the JVM
(that expects valid byte-code) it allows treating methods with only the return type being different as overloads. I am yet to look at the byte code and understand how that happens; it also seems that this will work only for generic code, so may be type erasure is slightly different in case of kotlin.
Now, things should be obvious why calling such a method from java fails. Kotlin offers a neat solution for this: @JvmName("someDistinctName")
. I am not entirely sure how this works under the hood either... yet, though I assume this will create a bridge method.
EDIT
@JvmName
will rename the method at the byte-code level.