为什么 Kotlin 不能在接口和派生自它的泛型类型之间进行智能转换?

Why can't Kotlin smart cast between an interface and the generic type that is derived from it?

我有以下 class:

abstract class PresenterActivity<S : ViewState, I : ViewIntent> : AppCompatActivity() { 
    open fun initViewIntent(): I {
        return object : ViewIntent{} // type mismatch on this line
    }
}

我收到预编译错误指出:

Type mismatch
Required: I
Found: <S, I>

为了修复这个预编译错误,我将 ViewIntent 对象转换为 I:

abstract class PresenterActivity<S : ViewState, I : ViewIntent> : AppCompatActivity() { 
    open fun initViewIntent(): I {
        @Suppress("UNCHECKED_CAST")
        return object : ViewIntent{} as I
    }
}

但为什么 Kotlin 不能检测到 I 必须派生自 ViewIntent 并进行智能转换?

只是因为 "I" 不一定派生自 ViewIntent,而恰恰是 ViewIntent class。

你可以这样修复:

abstract class PresenterActivity<S : ViewState, I : ViewIntent> : AppCompatActivity() { 
    open fun initViewIntent(): ViewIntent {
        return object : ViewIntent{} 
    }
}

按你的方式做真的很不安全。

要了解原因,我想您应该开始阅读以下内容:

https://blog.kotlin-academy.com/kotlin-generics-variance-modifiers-36b82c7caa39

https://kotlinlang.org/docs/reference/generics.html

https://proandroiddev.com/understanding-generics-and-variance-in-kotlin-714c14564c47

那是因为 ViewIntent 不是 I。参见示例:

class MyViewIntent : ViewIntent

class MyPresenterActivity : PresenterActivity<..., MyViewIntent>() {
    // inherited from PresenterActivity
    open fun initViewIntent(): ViewIntent {
        return object : ViewIntent{} as MyViewIntent // you see where this breaks
    }
}

基本上,您所做的事情不起作用的原因是因为 IViewIntent 的子 class。你的对象也是一个subclassViewIntent。这是一个完全不同的subclass。您正在执行的转换就像尝试将 StringBuilder 转换为 String.

现在让我们讨论一下我认为您 "want" 应该做什么以及为什么这也不起作用。为了真正得到你想要的结果,你需要直接创建I类型,像这样:

return object : I {}

我们用实际的 class、

替换了 I
return object : SomeClass {}

这肯定也会失败。 SomeClass 的构造函数需要被调用,而你没有这样做。在使用泛型类型时,无法知道将什么传递给该构造函数。