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

时间:2018-06-05 17:19:21

标签: kotlin kotlin-generics

我有以下课程:

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
    }
}

但为什么科特林无法检测到I必须来自ViewIntent并且聪明地施展它?

3 个答案:

答案 0 :(得分:8)

那是因为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
    }
}

答案 1 :(得分:5)

这只是因为&#34;我&#34;不一定是从ViewIntent派生的,而是从ViewIntent类派生的。

你可以这样解决:

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

答案 2 :(得分:1)

基本上,您正在执行的操作不起作用的原因是,IViewIntent的子类。您的对象也是ViewIntent的子类。这是一个完全不同的子类。您正在执行的强制转换就像尝试将StringBuilder强制转换为String

现在让我们讨论一下我认为您“想”做的事情以及为什么这也不起作用。为了真正获得所需的结果,您需要直接创建I类型,如下所示:

return object : I {}

然后我们将I替换为实际的类,

return object : SomeClass {}

这当然也会失败。 SomeClass的构造函数需要被调用,而您没有这样做。而且,没有办法知道使用泛型类型时要传递给该构造函数的内容。