在Kotlin中,以下代码编译:
class Foo {
fun bar(foo: List<String>): String {
return ""
}
fun bar(foo: List<Int>): Int {
return 2;
}
}
Run Code Online (Sandbox Code Playgroud)
但是,此代码不会:
class Foo {
fun bar(foo: List<String>): String {
return ""
}
fun bar(foo: List<Int>): String {
return "2";
}
}
Run Code Online (Sandbox Code Playgroud)
编译它会导致以下错误:
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
Run Code Online (Sandbox Code Playgroud)
在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";
}
}
Run Code Online (Sandbox Code Playgroud)
不出所料,两个先前的代码片段都会产生熟悉的编译器错误:
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
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,第一个Kotlin示例完全起作用,其次,如果它起作用,为什么第二个Kotlin示例会失败?Kotlin是否将方法的返回类型视为其签名的一部分?此外,与Java相比,为什么Kotlin中的方法签名遵循完整的参数类型?
Str*_*lok 19
实际上Kotlin知道你的例子中两种方法之间的区别,但jvm不会.这就是为什么它是一个"平台"冲突.
您可以使用@JvmName注释来编译第二个示例:
class Foo {
@JvmName("barString") fun bar(foo: List<String>): String {
return ""
}
@JvmName("barInt") fun bar(foo: List<Int>): String {
return "2";
}
}
Run Code Online (Sandbox Code Playgroud)
出于这个原因,这个注释就存在了.您可以在互操作文档中阅读更多内容.
虽然@Streloks 的回答是正确的,但我想更深入地了解它的工作原理。
第一个变体之所以起作用,是因为它在 Java 字节代码中没有被禁止。虽然 Java 编译器抱怨它,即Java 语言规范不允许它,但字节代码确实如此,正如https://community.oracle.com/docs/DOC-983207和https://www 中所述.infoq.com/articles/Java-Bytecode-Bending-the-Rules。在字节代码中,每个方法调用都引用了方法的实际返回类型,而在编写代码时并非如此。
不幸的是,我找不到实际的来源,为什么会这样。
关于Kotlins 名称解析的文档包含一些有趣的观点,但我没有在那里看到您的实际案例。
真正帮助我理解的是@Yole对Kotlin 类型擦除的回答——为什么函数仅在泛型类型上不同是可编译的,而那些仅在返回类型上不同的则不是?,更准确地说,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
Run Code Online (Sandbox Code Playgroud)
或者有一个提供泛型类型但甚至不使用它的方法:
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
Run Code Online (Sandbox Code Playgroud)
这也是以下方法不起作用的原因:
fun bar(foo: List<*>) = ""
fun bar(foo: List<*>) = 2
Run Code Online (Sandbox Code Playgroud)
这是不可编译的,因为它会导致冲突重载,因为在尝试识别要调用的方法时不考虑分配变量本身的类型:
val x : String = bar(listOf(null)) // ambiguous, type of x is not relevant
Run Code Online (Sandbox Code Playgroud)
现在关于名称冲突:只要您使用相同的名称、相同的返回类型和相同的参数(其泛型类型被删除),您实际上将在字节码中获得完全相同的方法签名。这就是为什么@JvmName变得必要。有了它,您实际上可以确保字节码中没有名称冲突。
| 归档时间: |
|
| 查看次数: |
2338 次 |
| 最近记录: |