Reputation: 3453
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.
Upvotes: 11
Views: 13020
Reputation: 120858
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.
Upvotes: 11
Reputation: 3863
An easy solution would be writing a helper method in Kotlin and just calling that.
Another way using only Java would be getting a MethodHandle
for both methods and using them:
MethodHandle MH_fooString = lookup().findStatic(ClassKt.class, "foo", methodType(String.class));
MethodHandle MH_fooT = lookup().findStatic(ClassKt.class, "foo", methodType(Object.class));
String foo = (String) MH_fooString.invokeExact();
It's not nearly as simple and requires handling exceptions though.
Upvotes: 1