在Kotlin中擦除是如何工作的?

在Kotlin中擦除是如何工作的?,kotlin,Kotlin,在Kotlin中,编译以下代码: class Foo { fun bar(foo: List<String>): String { return "" } fun bar(foo: List<Int>): Int { return 2; } } class-Foo{ 趣味酒吧(foo:List):字符串{ 返回“” } 趣味酒吧(foo:List):Int{ 返回2; } } 但是,该代码不: clas

在Kotlin中,编译以下代码:

class Foo {
    fun bar(foo: List<String>): String {
        return ""
    }

    fun bar(foo: List<Int>): Int {
        return 2;
    }
}
class-Foo{
趣味酒吧(foo:List):字符串{
返回“”
}
趣味酒吧(foo:List):Int{
返回2;
}
}
但是,该代码不:

class Foo {
    fun bar(foo: List<String>): String {
        return ""
    }

    fun bar(foo: List<Int>): String {
        return "2";
    }
}
class-Foo{
趣味酒吧(foo:List):字符串{
返回“”
}
趣味酒吧(foo:List):字符串{
返回“2”;
}
}
编译此文件将导致以下错误:

Error:(8, 5) Kotlin: Platform declaration clash: The following declarations have the same JVM signature (foo(Ljava/util/List;)Ljava/lang/String;):
    fun foo(layout: List<Int>): String
    fun foo(layout: List<String>): String
错误:(8,5)Kotlin:平台声明冲突:以下声明具有相同的JVM签名(foo(Ljava/util/List;)Ljava/lang/String;):
fun-foo(布局:列表):字符串
fun-foo(布局:列表):字符串
在Java中,两个示例都不会编译:

class Foo {
    String bar(List<Integer> foo) {
        return "";
    }

    Integer bar(List<String> foo) {
        return 2;
    }
}

class Foo {
    String bar(List<Integer> foo) {
        return "";
    }

    String bar(List<String> foo) {
        return "2";
    }
}
class-Foo{
字符串栏(列表foo){
返回“”;
}
整型条(列表foo){
返回2;
}
}
福班{
字符串栏(列表foo){
返回“”;
}
字符串栏(列表foo){
返回“2”;
}
}
毫不奇怪,前面的两个代码段都会生成常见的编译器错误:

Error:(13, 12) java: name clash: bar(java.util.List<java.lang.String>) and bar(java.util.List<java.lang.Integer>) have the same erasure
错误:(13,12)java:name冲突:bar(java.util.List)和bar(java.util.List)具有相同的擦除

让我惊讶的是,第一个Kotlin示例完全有效,第二个,如果有效,为什么第二个Kotlin示例会失败?科特林把一个方法的返回类型看作它的签名的一部分吗?此外,与Java相比,为什么Kotlin中的方法签名与完整参数类型有关?

实际上Kotlin知道示例中两种方法之间的区别,但jvm不会。这就是为什么这是一场“平台”冲突

您可以使用
@JvmName
注释编译第二个示例:

class Foo {
  @JvmName("barString") fun bar(foo: List<String>): String {
    return ""
  }

  @JvmName("barInt") fun bar(foo: List<Int>): String {
    return "2";
  }
}
class-Foo{
@JvmName(“barString”)乐趣条(foo:List):字符串{
返回“”
}
@JvmName(“barInt”)乐趣条(foo:List):字符串{
返回“2”;
}
}

这个注释的存在正是出于这个原因。您可以在中阅读更多。

虽然@Streloks的答案是正确的,但我想更深入地了解它的工作原理

第一个变体之所以有效,是因为它在Java字节码中没有被禁止。当Java编译器抱怨它时,也就是说,字节码会抱怨,正如和中所述。在字节码中,每个方法调用都引用方法的实际返回类型,而在编写代码时不是这样

不幸的是,我找不到真正的来源,为什么会这样

关于这个问题的文件包含了一些有趣的观点,但我没有看到你的实际案例

真正帮助我理解它的是to,更确切地说,kotlin编译器在决定调用哪个方法时不会考虑变量的类型

因此,这是一个经过深思熟虑的设计决策,即在变量上指定类型不会影响要调用的方法,而是相反,即被调用的方法(有或没有泛型信息)会影响要使用的类型

将规则应用于以下示例是有意义的:

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

val x = bar(listOf("")) --> uses (1), type of x becomes String
val y = bar(listOf(2))  --> uses (2), type of y becomes Int

现在关于名称冲突:只要您使用相同的名称、相同的返回类型和相同的参数(其泛型类型被删除),您就会在字节码中得到非常相同的方法签名。这就是为什么
@JvmName
变得必要的原因。这样,您就可以确保字节码中没有名称冲突。

但是,为什么第一个Kotlin示例在编译时没有此注释?JVM是否识别具有不同返回类型的方法?根据,返回类型不是方法签名的一部分。啊,我明白了。也许Java不允许在编译期间使用不同返回类型的相同名称/参数方法,但JVM可以识别它们?@breandan是的,返回类型是签名的一部分,但在第一种情况下,两个方法的返回类型不同(即使参数擦除类型相同),因此签名不同。它们是
foo(Ljava/util/List;)Ljava/lang/String
foo(Ljava/util/List;)Ljava/lang/Integer分别为。Java只考虑名称冲突检查中的参数类型和方法名称,因此Java中的第一个示例不是编译。这更多的是关于重载和anot类型擦除,并且可以很容易地用语言修复(就像Kotlin那样)。我不认为Kotlin将方法的返回类型视为其类型签名的一部分。例如,它不允许在同一个类中定义两个方法,
@JvmName(“barInt”)乐趣栏(foo:List):Int
@JvmName(“barString”)乐趣栏(foo:List):String
,即使它们的JVM签名不同(
barInt(Ljava/util/List;)Ljava/lang/Integer;
barString(Ljava/util/List;)Ljava/lang/String;
分别)。但是,它将允许
@JvmName(“barInt”)乐趣条(foo:List):Int
@JvmName(“barString”)乐趣条(foo:List):String
在同一个类中。这是因为重载解析永远不能选择这些方法中的一个而不是另一个(它不考虑返回类型),所以两个方法都不能被调用。你是说kotlin中只允许按返回类型重载方法吗?因为如果你这样做了,你会发现这个
fun go():String=“”;fun go():Int=12必须编译,但它不能不,我不想说。。。我的意思是“第一个样本中显示的星座”和“它是允许的”。。。我将重新表述这一点。。。。Kotlin确实有一种处理泛型的特殊方法。。。我不明白为什么以下方法有效:
fungo():String=“”;fun go():Int=12
。。。我是说。。。我甚至没有在第二个中使用
T
。。。另一方面如果这是有效的,为什么您提供的示例不起作用呢?带有
的示例是我正在研究的一个问题…:)修改了我的答案。。。让我知道现在是否更清楚了我没有解释
fun bar(foo: List<*>) = ""         (3)
fun <T> bar(foo: List<*>) = 2      (4)

val x = bar(listOf(null))          --> uses (3) as no generic type was specified when calling the method, type of x becomes String
val y = bar<String>(listOf(null))  --> uses (4) as the generic type was specified, type of y becomes Int
fun bar(foo: List<*>) = ""
fun bar(foo: List<*>) = 2
val x : String = bar(listOf(null)) // ambiguous, type of x is not relevant