【问题标题】:Kotlin - type mismatch with generic return type and subclassKotlin - 与通用返回类型和子类的类型不匹配
【发布时间】:2020-03-29 22:37:01
【问题描述】:

我希望每个人都在这场大流行中保持健康:)

TIA,我可以使用一些帮助来找出我做错了什么!另外,我对 Kotlin 有点陌生,如果这是一个愚蠢的问题,我很抱歉。

我正在尝试扩展一个类并实现在 java 库 (AndroidX) 中定义的接口。但是,当我尝试实现接口函数时,我的返回值出现类型不匹配错误。由于我使用的是 AndroidX 库中定义的接口,因此不能简单地更改接口函数签名以使其构建。

我基本上将这个其他 SO 答案转换为 Kotlin:https://stackoverflow.com/a/34398747/535638

开发机器/IDE 信息: 安卓工作室 3.6.1 构建 #AI-192.7142.36.36.6241897,构建于 2020 年 2 月 26 日 运行时版本:1.8.0_212-release-1586-b04 amd64 虚拟机:JetBrains s.r.o 的 OpenJDK 64 位服务器虚拟机 视窗 8.1 6.3 GC:ParNew、ConcurrentMarkSweep 内存:1237M 核心:8 注册表:ide.new.welcome.screen.force=true 非捆绑插件:org.jetbrains.kotlin、com.google.services.firebase

我试图用下面的代码概括这个错误,希望能在你理解另一个 SO 问题时节省一些精力,而且我在这个概括的代码中遇到了同样的编译器错误。

open class LibraryClassA {

}

open class LibraryClassB {
    val retval = LibraryClassA()

    // this kotlin signature seems to be inferred/transcribed from java by the IDE/compiler
    open fun <T : LibraryClassA?> foobar(): T? {
        return null
    }
}

class MyClass : LibraryClassB() {

    override fun <T : LibraryClassA?> foobar(): T? {
        return retval // compiler error on this line - type mismatch, Required T?, Found LibraryClassA
    }
}

【问题讨论】:

  • 也许这是一个愚蠢的问题...我能够转换为 T return retval as T,它创建了一个未经检查的转换警告,但它似乎运行正常。我不确定为什么 kotlin 编译器在没有显式转换的情况下无法处理这种情况。也不确定转换为泛型类型有多大意义,所以我希望对 kotlin 更了解的人可以谈谈。如果没有其他人回复,我会给它一些时间并稍后将其作为答案发布。
  • "这个 kotlin 签名似乎是从 java 推断/转录的" 来自哪一部分?我在链接的答案中没有看到任何类似的东西。
  • 我的意思是说 kotlin 编译器正在生成它作为要覆盖的签名,在我看来,这是一个在 androidx 库中用 java 编写的接口。当我在 kotlin 中覆盖该 java 函数时,这就是为我自动生成的。
  • 请指向具体的界面,以便我们查看它的文档以及它的用途。

标签: java android generics kotlin android-jetpack


【解决方案1】:

另一个答案解释了编译器错误背后的原因,但由于我无法轻松修改我正在使用的库,我的问题的解决方案是进行强制转换并抑制警告。

我的最终代码如下所示:

override fun <T : Preference?> findPreference(key: CharSequence): T? {

    // unchecked cast needed since we can't modify the library code to call this with the
    // correct type parameter, and we can't modify the function signature
    @Suppress("UNCHECKED_CAST")
    return this.preference as T
}

【讨论】:

    【解决方案2】:

    这是根本问题。

    当你写作时

    fun <T : LibraryClassA?> foobar(): T? { ... }
    

    您声明foobar调用者 可以指定他们想要的LibraryClassA 的任何子类,并获得that 的实例。例如,如果有

    class FooClass : LibraryClassA() { ... }
    

    有人可以打电话

    val myFooClass: FooClass = myLibraryClassB.foobar<FooClass>()
    

    并期望它起作用。您所拥有的将为此工作,因为您将拥有的是 LibraryClassA 实例,不是 FooClass 实例。

    如果您的意图是返回LibraryClassA?一些 子类,但LibraryClassB 的实现控制他们得到什么,而不是调用者,那么该函数的正确类型是

    fun foobar(): LibraryClassA? { ... }
    

    【讨论】:

    • 我不能说出接口的意图,因为我不是作者(它在 androidx 库中定义),但我的猜测是它是一个定位器函数来获取类成员is-a LibraryClassA
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多