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-substitution代替base
本身。它将允许从Tile[Color]
使用buildTile
。但是,这种方法会解开base
到Tile[_ >: Color]
满足里氏-取代Tile[+A]
本身:
scala> a.buildTile("", 1.0, a: Tile[Any]) //Tile[Any] also works
None
res27: Tile[Color] = BlueTile(,None)
因此,我建议限制A
到Color
(或subtraits也许某处OD 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])
^