Kotlin 泛型函数和逆变

Kotlin generic functions and contravariance

我无法解决一个看似简单的问题。我有 Reply class,它可以包含不同的响应类型 - Views:

interface View
data class NiceView(val name: String) : View

class MyReply<T : View>(view: T)

现在,我想编写一个接受命令的函数,并return根据命令生成适当的视图:

fun <T : View> handle(command: Command): Reply<T> {
   return Reply(NiceView(""))
}

我在这里看到两个问题:

  1. 我在 return 类型 Reply<T> 上遇到了这个奇怪的错误,但这只发生在这个玩具示例中,而不是我的生产代码中:

    Type argument is not within its bounds.
    Expected: View
    Found: T
    
  2. returning Reply() 时出错。这是杀死我大脑的东西,这是我在生产代码中看到的:

    Type mismatch.
    Required: T
    Found: NiceView
    Type mismatch.
    Required: Reply<T>
    Found: Reply<NiceView>
    Type mismatch.
    Required: View
    Found: NiceView
    

我确实尝试使用 inout 关键字搞乱协变和逆变,但无济于事。有人可以在这里为我指明正确的方向吗?

我无法重现第一个问题,但可以通过简单地在 Reply class 或 return 的通用参数中添加 in modifier 来解决第二个问题handle 函数的值:

interface View
data class NiceView(val name: String) : View

class Reply<in T : View>(view: T)

fun <T : View> handle(command: Command): Reply<T> {
    return Reply(NiceView(""))
}

// OR

class Reply<T : View>(view: T)

fun <T : View> handle(command: Command): Reply<in T> {
    return Reply(NiceView(""))
}

in修饰符表示逆变(类似于Java中的“?super T”)。

问题是 handle() 是说 T 是一个通用类型,它是 View 的子类。所以 T 可以是 View 任何 子类,但你试图强制它成为 NiceView。这意味着像这样的代码将不起作用:

val otherReply: Reply<OtherView> = handle(command)

handle 试图 return NiceView,但通用参数说它应该是 OtherView.

如果你想保证你的函数总是returnReply<T : View>,你需要构造一个泛型类型的实例T。这意味着您必须以某种方式识别 View 实现。我不知道您的其余代码是什么样的,但这是一个接受 view 作为参数的示例:

fun <T : View> handle(command: Command, view: T): Reply<T> {
    return Reply(view)
}

fun main() {
    val otherReply: Reply<OtherView> = handle(command, OtherView(""))
}

或者如果你不关心允许调用者指定视图类型,你根本不需要泛型参数:

fun handle(command: Command): Reply<View> {
    return Reply(NiceView(""))
}

fun <T : View> handle(command: Command): Reply<T> { 不是 表示“接受命令和returns 适当视图”的功能。它的意思是“接受一种视图类型的函数——和一个命令——以及 returns 对该类型视图的回复。” handlecaller 可以选择它想要获得的 View 的任何类型,这不是你想要的——也不是你已经实现的,因为用户可能想要 NiceView.

以外的东西

根据您的既定目标,此代码中 handle 函数的适当类型是

fun handle(command: Command): Reply<*>