简体   繁体   中英

Kotlin method overloading

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.

You can use @JvmName to differentiate the code when it's called from java:

@JvmName("fooString")
fun foo(): String = "foo_1"

fun <T> foo(): T = "foo_2" as T

This will allow calling the String method in Java with ClassKt.fooString() , which resolves the clash.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM