繁体   English   中英

在协变位置上使用协变类型是否有其他选择?

[英]Is there any alternatives to using covariant type in contravariant position?

我正在寻找一种从现有实例创建新实例的模式,这样我就可以通过遍历其上方的层次结构来计算Tile的UltimateBase。 我尝试了以下操作,但是在buildTile方法的参数中获得了“协变类型A在协变位置出现”的信息 是否有替代模式来构建依赖于先前实例的实例?

trait Color
trait Blue extends Color
trait Green extends Color

trait Tile[+A] {
  def declaredBase: Option[(Double, Tile[A])]

  final val ultimateBase: Option[(Double, Tile[A])] = ??? // Some implementation, not important

  // Can't be protected[this]
  def buildTile(name: String, multiple: Double, base: Tile[A]): Tile[A]
}

case class BlueTile(name: String, declaredBase: Option[(Double, Tile[Blue])]) extends Tile[Blue] {
  override def buildTile(name: String, multiple: Double, base: Tile[Blue]): Tile[Blue] = {
    // Something more complicated here
    this
  }
}

谢谢。

1)您可以分别声明协变位置的类型,并在子类中实现它:

trait Tile[+A] {
  type T <: A
  def declaredBase: Option[(Double, Tile[A])]

  final val ultimateBase: Option[(Double, Tile[A])] = None // Some implementation, not important

  // Can't be protected[this]
  def buildTile(name: String, multiple: Double, base: Tile[T]): Tile[A]
}

case class BlueTile(name: String, declaredBase: Option[(Double, Tile[Blue])]) extends Tile[Blue] {
  type T = Blue
  override def buildTile(name: String, multiple: Double, base: Tile[T]): Tile[Blue] =
  {
    println(base.declaredBase)// Something more complicated here
    this
  }
}

scala> val x = new BlueTile("", None)
x: BlueTile = BlueTile(,None)

scala> val a: Tile[Color] = new BlueTile("", None).buildTile("", 1.0, x) //covariance works
None
a: Tile[Color] = BlueTile(,None)

它在这种特殊情况下有效,因为您实际上可以最终指定类型T。

2)另一种方法是隐式添加def buildTile

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Tile[+A] {  
  def declaredBase: Option[(Double, Tile[A])]
  final val ultimateBase: Option[(Double, Tile[A])] = None // Some implementation, not important 
}

case class BlueTile(name: String, declaredBase: Option[(Double, Tile[Blue])]) extends Tile[Blue]

implicit class BuildFromBlue(t: Tile[Blue]) {
  def buildTile(name: String, multiple: Double, base: Tile[Blue]): Tile[Blue] = {
    println(base.declaredBase)// Something more complicated here
    t
  }
}

// Exiting paste mode, now interpreting.

defined trait Tile
defined class BlueTile
defined class BuildFromBlue

scala> val x = new BlueTile("", None)
x: BlueTile = BlueTile(,None)

scala> val a: Tile[Color] = new BlueTile("", None).buildTile("", 1.0, x) //covariance works
None
a: Tile[Color] = BlueTile(,None)

3A)最后一种选择是通过存在类型的协方差。 您可以将Tile[A]声明为不变式,但在需要时需要Tile[_ <: A]

trait Tile[A] {
  def declaredBase: Option[(Double, Tile[A])]
  def buildTile(name: String, multiple: Double, base: Tile[A]): Tile[A]
}

case class BlueTile(name: String, declaredBase: Option[(Double, Tile[Blue])]) extends Tile[Blue] {
  override def buildTile(name: String, multiple: Double, base: Tile[Blue]): Tile[Blue] = this
}

scala> val x = new BlueTile("", None)
x: BlueTile = BlueTile(,None)

scala> val a: Tile[_ <: Color] = new BlueTile("", None).buildTile("", 1.0, x)
a: Tile[_ <: Color] = BlueTile(,None)

1,2,3A)但是在转换为更大的Tile[Color]类型后,不能在相反的位置使用它:

scala> a.buildTile("", 1.0, a)
<console>:18: error: type mismatch;
found   : Tile[_$1(in value res20)] where type _$1(in value res20) <: Color
required: Tile[_$1(in value a)]
          a.buildTile("", 1.0, a)
                               ^

3B)您只需将base与变量存在类型绑定即可实现:

trait Tile[+A] {
  def declaredBase: Option[(Double, Tile[A])]
  def buildTile(name: String, multiple: Double, base: Tile[_ >: A]): Tile[A]
}

case class BlueTile(name: String, declaredBase: Option[(Double, Tile[Blue])]) extends Tile[Blue] {
  override def buildTile(name: String, multiple: Double, base: Tile[_ >: Blue]): Tile[Blue] = {println(base.declaredBase); this}
}

scala> val x = new BlueTile("", None)
x: BlueTile = BlueTile(,None)

scala> val a: Tile[Color] = new BlueTile("", None).buildTile("", 1.0, x: Tile[Blue])
None
a: Tile[Color] = BlueTile(,None)

scala> a.buildTile("", 1.0, a) //you can do it now
None
res23: Tile[Color] = BlueTile(,None)

它的工作原理是[_ >: A]实际上是说您不需要liskov替代来作为base 它将允许从Tile[Color]使用buildTile 但是,这种方法将未结合的baseTile[_ >: Color]满足里氏取代为Tile[+A]本身:

scala> a.buildTile("", 1.0, a: Tile[Any]) //Tile[Any] also works
None
res27: Tile[Color] = BlueTile(,None)

因此,我建议将A限制为Color (或可能在Tile某处):

trait Tile[+A <: Color]{...}
...

scala> a.buildTile("", 1.0, a) 
None
res33: Tile[Color] = BlueTile(,None)

scala>  a.buildTile("", 1.0, a: Tile[Any])
<console>:18: error: type arguments [Any] do not conform to trait Tile's type parameter bounds [+A <: Color]
           a.buildTile("", 1.0, a: Tile[Any])
                                   ^

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM