将反变型参数用于方法的类型参数和返回类型时的困难

时间:2017-06-24 14:46:50

标签: scala generics contravariance

我正在尝试在scala中构建一个行为树库,但我遇到了方差问题。以下是节点的简化版本:

sealed trait State[+O]
case class Running[+O](curOut: O, node: Node[O,_]) extends State[O]
case class Success[+O](out: O) extends State[O]
case object Failure extends State[Nothing]

trait Node[-I,C] {
    protected def canRun(item: I, context: C, tick: Int): Boolean
    protected def run[O <: I](item: I, context: C, tick: Int): State[O]

    def apply[O <: I](item: I, context: C, tick: Int): State[O] = {
        if (canRun(item, context, tick)) run(item, context, tick)
        else Failure
    }
}

每当我尝试实例化Node时,它会抱怨method run overrides nothing

val node = new Node[Int,Any] {

    override protected def run(item: Int, context: Any, tick: Int): State[Int] = ???

    override protected def canRun(item: Int, context: Any, tick: Int): Boolean = ???
} 

我已尝试将O中的Node更改为类型成员,但抱怨Covariant I is used in contravariant position

type O <: I

我想问的是,如何使用逆变型参数作为方法参数的类型和方法返回类型?理想情况下,我不希望在父节点和装饰器节点中为可重用性提供I逆变。

提前致谢

2 个答案:

答案 0 :(得分:2)

回答你的问题

  

如何使用逆变类型参数作为方法参数的类型和方法返回类型?

你不能,在方法参数和返回类型中都不能使用conta-(co)-variant类型参数。

我建议解决此问题的方法是从node案例类中删除Running参数。或者用只读特性替换参数。例如:

case class Running[+O](curOut: O, process: Process[O]) extends State[O]
trait Process[+O] {
  def currentState: State[O]
}

答案 1 :(得分:2)

  

它抱怨方法运行会覆盖任何内容

那是因为覆盖方法必须具有相同的类型参数,您只需替换IC,所以在这种情况下它必须是

new Node[Int,Any] {

    override protected def run[O <: Int](item: Int, context: Any, tick: Int): State[O] = ???

    override protected def canRun(item: Int, context: Any, tick: Int): Boolean = ???
}

请注意,此签名表示&#34;来电者可以获得State[O]所需的O <: Int&#34; Nothing。您无法真正实现具有此签名的方法。

如果您想为run获取此签名,那么在Node中它可能应该是

protected def run(item: I, context: C, tick: Int): State[I]

这意味着I不能反变(或协变,就此而言)。