2015-02-11 43 views
2

我要寻找一种模式,从现有的创建新实例,这样我可以通过遍历它上面的层次结构计算瓷砖的ultimateBase。我尝试以下但获得参数buildTile方法的基地“协变型在逆变位置发生”。是否有一种替代模式来构建依赖于先前实例的实例?在逆变位置使用协变类型有没有其他选择?

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 
    } 
} 

谢谢。

回答

3

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。但是,这种方法会解开baseTile[_ >: Color]满足里氏-取代Tile[+A]本身:

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

因此,我建议限制AColor(或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]) 
           ^
相关问题