Kotlin认为两个方法具有相同的JVM签名,但实际上没有

时间:2018-07-29 12:05:51

标签: kotlin

我来自C#的背景,我知道如何在C#中实现它,但是我在Kotlin方面苦苦挣扎。 我有2个扩展功能:

fun <T> Foo<T>.myFunction(func: () -> Unit): Foo<T>

fun <T> Foo<T>.myFunction(func: () -> Foo<T>): Foo<T>

很明显,func的返回类型在两个函数中都不同。第一个函数执行它并返回它,第二个函数执行func并返回func的结果。 但这给了我一个错误:

“平台声明冲突:以下声明具有相同的JVM签名”。

如何在Kotlin中正确实现此目的?

2 个答案:

答案 0 :(得分:5)

在JVM上,我们必须contend with type erasure。从本质上讲,这意味着类型(在这种情况下为T)在已编译的字节码中被丢弃,并且所需的检查仅在编译时进行。鉴于此,您必须牢记这一点。

在两种情况下,Kotlin都会将函数参数定义为Function0。由于类型被删除,因此() -> Unit() -> Foo<T>在字节码中看起来相同。我们可以通过反编译您提供的代码来证明这一点(我将其重命名为myFunction2其中之一,以使其正常工作):

public final class com/ginsberg/KotlinStuffKt {
    public final static myFunction(Lcom/ginsberg/Foo;Lkotlin/jvm/functions/Function0;)Lcom/ginsberg/Foo;

    public final static myFunction2(Lcom/ginsberg/Foo;Lkotlin/jvm/functions/Function0;)Lcom/ginsberg/Foo;

}

这是Kotlin编译器生成的(它做的更多,但是我从此示例中删除了非必要的部分)。如您所见,由于类型擦除,我们的类型不见了。而且,如果我们撤消更改(myFunction2变成myFunction),则根本无法将这些区别开来。这就是编译器所抱怨的-如果您擦除JVM无法区分这些功能的类型。

答案 1 :(得分:4)

由于类型擦除(内部 Function0<T> 类用于表示函数参数),因此您的函数在JVM中具有冲突的签名;您可以通过为它们每个人指定JVM名称来解决此问题。从Kotlin,您仍然可以使用原始名称访问它们,但是实际上从Java或内部使用了另一个名称。只需在备用版本上使用@JvmName批注:

fun <T> Foo<T>.myFunction(func: () -> Unit): Foo<T>

@JvmName("myfunctionWithFoo")   
fun <T> Foo<T>.myFunction(func: () -> Foo<T>): Foo<T>