【问题标题】:Kotlin variance issueKotlin 方差问题
【发布时间】:2018-12-20 00:26:23
【问题描述】:

我在玩节点链的概念,每个节点都可以有不同的输入和输出类型(尽管输入/输出类型都来自共享的父类型),其中数据从节点传递节点并沿途变换。这是一个例子:

open class Shape
open class Oval : Shape()
class Circle : Oval()

class NodeChain {
    val nodes: MutableList<Node<in Shape, out Shape>> = mutableListOf()

    fun node(n: Node<in Shape, out Shape>) {
        val prevNode = nodes.lastOrNull()
        nodes.add(n)
        prevNode?.nextNode = n::processInput
    }

    fun processInput(input: List<Shape>) {
        nodes[0].processInput(input)
    }
}

abstract class Node<InputType : Shape, OutputType : Shape> {
    var nextNode: Function1<List<OutputType>, Unit>? = null

    abstract fun processInput(input: List<InputType>)
}

class OvalNode : Node<Shape, Oval>() {
    override fun processInput(input: List<Shape>) {
        nextNode?.invoke(listOf(Oval()))
    }
}

class CircleNode : Node<Oval, Circle>() {
    override fun processInput(input: List<Oval>) {
        nextNode?.invoke(listOf(Circle()))
    }
}

val nodeChain = with (NodeChain()) {
    node(OvalNode())
    node(CircleNode()) // Error[1] here (listed below)
    this
}

Erorr[1]:
Type mismatch. 
Required:
Scratch_3.Node<in Scratch_3.Shape, out Scratch_3.Shape>
Found:
Scratch_3.CircleNode

您可以在最后看到我收到一个错误:尝试将“CircleNode”添加到链中时,它抱怨期待Node&lt;in Shape, out Shape&gt; 并且不允许CircleNode(即@987654324 @)。我对方差的理解还不足以弄清楚我需要什么来实现这一点(或者它是否可能?)。这可能吗?我在正确的轨道上吗?

【问题讨论】:

  • in Shape 只允许 Shape 的超类型,而不是子类型。 Node&lt;Shape, Circle&gt; 会工作。 Node&lt;Oval, Shape&gt; 不会。
  • 感谢@LouisWasserman,有没有办法做到这一点?这里有其他的输入/输出组合吗?
  • 我怀疑您是否可以拥有您想要的NodeChain 类型。 Node 中的链接结构及其nextNode 字段有效,但您不能让with 结构像您希望的那样工作,因为允许下一个节点的类型会随着您添加更多节点而改变。 Node 单独应该可以工作,直接构建 Nodes 链而不需要中间类型应该可以工作。
  • 感谢@LouisWasserman,是的,创建和连接节点(没有任何管理链)确实可以正常工作,但希望能够利用“链”来拥有节点并组装它们......我担心,虽然这可能是不可能的:/

标签: kotlin covariance contravariance


【解决方案1】:

您使用的类型Node&lt;in Shape, out Shape&gt; 等价于Node&lt;Shape, *&gt;。因此,您只能添加具有InputTypeShape 的类型。对于您的用例,您应该使用Node&lt;*, *&gt; 添加Node 的任何扩展:

class NodeChain {
    val nodes: MutableList<Node<*, *>> = mutableListOf()

    fun node(n: Node<*, *>) {
        val prevNode = nodes.lastOrNull()
        nodes.add(n)
        prevNode?.nextNode = n::processInput
    }

    fun processInput(input: List<Shape>) {
        nodes[0].processInput(input)
    }
}

【讨论】:

  • 谢谢@tynn,我之前试过这个,但在尝试调用 processInput 时出现错误:Type mismatch. Required: List&lt;Nothing&gt; Found: List&lt;Scratch_3.Shape&gt; Projected type Scratch_3.Node&lt;*, *&gt; restricts use of public abstract fun processInput(input: List&lt;InputType&gt;): Unit defined in Scratch_3.Node
  • 你的实现中是否还有逆变?
  • 据我所知,不...这只是我在上面粘贴的代码,但所有 in/out 已从 NodeChain 中删除。 Node 没有。它对你有用吗?我再检查一下。
  • 问题是,List&lt;InputType&gt;in 位置上有InputType。所以逆变是隐式给出的。
猜你喜欢
  • 2022-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-22
  • 2011-04-15
相关资源
最近更新 更多