gpt4 book ai didi

Kotlin 允许与具有不同返回类型的属性 getter 相同的函数签名

转载 作者:行者123 更新时间:2023-12-04 11:50:24 25 4
gpt4 key购买 nike

更新 2020-12-23
起源描述有点困惑。 Kotlin 不仅允许在子类中使用 getter 相同的签名,而且在自类中也允许使用相同的签名。所以这也是允许的:

open class BaseRequest {
val params = mutableMapOf<String, String>()
fun getParams(): List<String> {
return params.values.toList()
}
}
正如@Slaw 所说,这是 kotlin 编译器的行为,并且它可以工作,因为 JVM 使用地址而不是“签名”调用正确的方法。

我遇到了一种情况,似乎 Kotlin 允许子类创建与父类(super class)的 getter 相同的签名。
通常,函数具有相同的签名并且不允许不同的返回类型。所以我对这种情况感到困惑。我不确定这是否是故意设计的。
这是一个示例:
open class BaseRequest {
val params = mutableMapOf<String, String>()

init {
params["key1"] = "value1"
}
}

class SpecificRequest : BaseRequest() {
init {
params["key2"] = "value2"
}

fun getParams(): List<String> {
return params.values.toList()
}
}
MediatorRequest 有一个函数 getParams()它与它的父类(super class)具有相同的签名但具有不同的返回类型。在使用这个函数时,子类和父类(super class)似乎有相同声明的不同实现。
fun main() {
val specificRequest = SpecificRequest()
println("specificRequest.params: ${specificRequest.params}")
println("specificRequest.getParams(): ${specificRequest.getParams()}")
println("(specificRequest as BaseRequest).params: ${(specificRequest as BaseRequest).params}")
}
输出将是这样的:
specificRequest.params: {key1=value1, key2=value2}
specificRequest.getParams(): [value1, value2]
(specificRequest as BaseRequest).params: {key1=value1, key2=value2}
如果我们查看反编译的Java代码,有两个方法具有相同的签名和不同的返回类型,这在Java中确实是不允许的。
public class BaseRequest {
@NotNull
private final Map params;

@NotNull
public final Map getParams() {
return this.params;
}

/* ... */
}


public final class SpecificRequest extends BaseRequest {
@NotNull
public final List getParams() {
return CollectionsKt.toList((Iterable)this.getParams().values());
}
/* ... */
}
我知道函数名不合适,但存在潜在风险,如果我们在 .java 中使用 SpecificRequest,在将实例转换为它的父类(super class)之前,我们无法访问 Map 参数。这可能会导致误解。

最佳答案

Java 语言和 JVM 之间存在差异。 Java 语言不允许在同一个类中声明两个名称相同但返回类型不同的方法。这是语言的限制。然而,JVM 完全有能力区分这两种方法。而且由于 Kotlin 是它自己的语言,因此它不必遵循与 Java 完全相同的规则——即使是针对 JVM(因此编译为字节码)。
考虑以下 Kotlin 类:

class Foo {
val bar = mapOf<Any, Any>()
fun getBar() = listOf<Any>()
}
如果你编译这个类然后用 javap 检查字节码你会看到的:
Compiled from "Foo.kt"
public final class Foo {
public final java.util.Map<java.lang.Object, java.lang.Object> getBar();
public final java.util.List<java.lang.Object> getBar();
public Foo();
}
所以这两个函数肯定存在,尽管名称相同。但是,如果您访问该属性并调用该函数,您将看到:
fun test() {
val foo = Foo()
val bar1 = foo.bar
val bar2 = foo.getBar()
}
变成:
 public static final void test();
descriptor: ()V
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=2, locals=3, args_size=0
0: new #8 // class Foo
3: dup
4: invokespecial #11 // Method Foo."<init>":()V
7: astore_0
8: aload_0
9: invokevirtual #15 // Method Foo.getBar:()Ljava/util/Map;
12: astore_1
13: aload_0
14: invokevirtual #18 // Method Foo.getBar:()Ljava/util/List;
17: astore_2
18: return
这表明字节码知道要调用哪个函数。 JVM 可以处理这个问题。
但有一个警告。以下将无法编译:
class Foo {
fun getBaz() = mapOf<Any, Any>()
fun getBaz() = listOf<Any>()
}
为什么?我并不乐观,但我相信这与语法有关。 Kotlin 编译器总是可以根据您是否使用 foo.bar 轻松判断您打算调用哪个函数。或 foo.getBar() .但是调用两个 getBaz()的语法是一样的函数,这意味着编译器无法轻易分辨出您打算在所有情况下调用哪个函数(因此它不允许上述情况)。

关于Kotlin 允许与具有不同返回类型的属性 getter 相同的函数签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65421518/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com