当前位置: 首页 > 知识库问答 >
问题:

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

乐正远航
2023-03-14

更新2020-12-23

open class BaseRequest {
    val params = mutableMapOf<String, String>()
    fun getParams(): List<String> {
        return params.values.toList()
    }
}

正如@slaw所说,这是kotlin编译器的行为,它是有效的,因为JVM使用地址而不是“签名”调用正确的方法。

我遇到了一种情况,似乎Kotlin允许子类创建与超类的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()
    }
}
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中是不允许的。

java prettyprint-override">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,在将实例强制转换为它的超类之前,我们无法访问映射参数。而这可能会导致误解。

共有1个答案

宇文温文
2023-03-14

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

但有一个警告。以下内容将无法编译:

class Foo {
    fun getBaz() = mapOf<Any, Any>()
    fun getBaz() = listOf<Any>()
}

为什么?我不肯定,但我相信这与语法有关。Kotlin编译器总是可以根据您是使用foo.bar还是foo.getbar()轻松地判断要调用哪个函数。但是调用这两个getbaz()函数的语法是相同的,这意味着编译器不能在所有情况下很容易地判断您要调用哪个函数(因此它不允许上述操作)。

 类似资料:
  • 我在一次面试中被问到以下问题: 问题:名称和签名相同但返回类型不同的方法。他问我,可能吗?这种类型叫什么。 有人能告诉我以下情况吗: > 上面的事情在任何情况下都是可能的(至少像一个在基类中,一个在派生类中?)如果是,是什么类型?比如编译或运行时多态? 在编译时多态性中,如果方法的返回类型与签名也不同,该怎么办?但只有函数的名称是相同的。还是编译时多态性吗? 在重写中,如果我有不同的返回类型,但方

  • 我说的是Java,但这个概念也适用于其他语言-- 我们在同一个类上有两个方法,它们具有相同的基本功能,但提供不同的返回类型。这两种方法都会给你所有的东西,但一个得到一个可以提供对所有东西的访问的可迭代的,一个得到一个包含所有东西的集合。 我们知道你不能这样做(因为它不会编译): 那么,有人想过如何命名这些方法吗?这似乎是一个简单的解决方法: 然而,这显然有些冗长,可能不是最好的解决方案。我问的这个

  • 我需要编写一个java方法来从一个对象中获取特定的信息。但是,该对象可以是A类型的,也可以是B类型的。下面是我的代码的一部分: 当我这样写它时,它会引发一个错误,说“重复方法”。我怎么才能让这个起作用?

  • 问题内容: 我正在尝试使用RSA 7.5和Websphere 7服务器开发IBM JAX_WS Web服务。因为我是一个初学者,所以我遵循Java类优先方法,即首先创建Java类,然后生成WSDL文件。 当我尝试创建wsdl文件时,出现异常: java.security.PrivilegedActionException:com.sun.xml.internal.bind.v2.runtime.I

  • 问题内容: 我目前正在使用Gson用Java编写RSS feed解析器。我正在将RSS的XML转换为JSON,然后使用Gson将JSON反序列化为Java POJO(有点回旋,但是有原因)。就下面列出的Feed#1( BBC )进行反序列化而言,一切工作正常,但是对于下面列出的Feed#2( NPR ),我开始抛出异常。 我认为我已经确定了问题,但不确定如何解决该问题: 这是由于以下两个RSS F

  • 假设您有2个包,并且有名为-Test的公共类。 第一个包。测试 第二包测试 它们中的每一个都有实例变量-x。 在第一种情况下-int x=2; 在第二种情况下-int x=3; 我想导入FirstPackage。在第二个包内测试。测试并打印值为2的x。 我的代码: 但是输出是3。如何打印?