【问题标题】:Kotlin generic functions and contravarianceKotlin 泛型函数和逆变
【发布时间】:2021-01-22 13:37:45
【问题描述】:

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

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

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

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

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

我可以在这里看到两个问题:

  1. 我在返回类型 Reply&lt;T&gt; 上收到了这个奇怪的错误,但这仅发生在这个玩具示例中,而不是我的生产代码中:

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

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

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

【问题讨论】:

标签: kotlin generics contravariance


【解决方案1】:

fun &lt;T : View&gt; handle(command: Command): Reply&lt;T&gt; { 不是 表示“接受命令并返回适当视图”的函数。它的意思是“一个接受一种视图类型的函数——和一个命令——并返回该类型视图的回复。” handle调用者可以选择它想要获得的任何类型的View,这不是你想要的——也不是你已经实现的,因为用户可能想要@以外的东西987654324@.

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

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

【讨论】:

  • 完美!感谢您的建议!这是我需要的atm! :)
  • 还有一件事:解释得很好,再次感谢@Louis!
【解决方案2】:

我无法重现第一个问题,但只需将in modifier 添加到Reply 类的泛型参数或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”)。

【讨论】:

  • 这很有帮助,谢谢!我过度简化了我的示例,因为在我的 Reply 类中,我还有一个函数,它采用 (T) -&gt; String 类型的函数(如果你愿意,可以使用序列化器)并且不允许我将 T 指定为逆变 :(
【解决方案3】:

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

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

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

如果你想保证你的函数总是返回Reply&lt;T : View&gt;,你需要构造一个泛型类型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(""))
}

【讨论】:

  • 有道理,谢谢!我实际上会向您提出第二个建议,但是在我的情况下,由于某种原因过于简单化了它并不起作用。我现在实际上正在返回Either&lt;Error, Reply&lt;*&gt;&gt;。出于某种原因,它会让我将其指定为 Either&lt;Error, Reply&lt;View&gt;&gt;
猜你喜欢
  • 2021-12-09
  • 2013-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多