Scala协变容器

时间:2014-06-11 11:13:38

标签: scala covariance

我从Scala食谱中看到了一个例子。我理解了这个例子,并做了一些修改,以确保我彻底理解了这个例子。

代码示例

trait Animal {
    def speak
}

class Dog(var name: String) extends Animal {
    def speak { println("Dog says woof") }
}

class SuperDog(name: String) extends Dog(name) {
     override def speak { println("I'm a SuperDog") }
} 

// solution 1
class Container[+T](val elem : T) 

def makeDogsSpeak(dogHouse: Container[Dog]) {
    dogHouse.elem.speak
}

val superDogHouse = new Container(new SuperDog("Wonder Dog")) 
makeDogsSpeak(superDogHouse) 

// solution 2
class Container[T](val elem : T)  // remove covariant from type 

def makeDogsSpeak[U <: Dog](dogHouse: Container[U]) {
    dogHouse.elem.speak
}

val superDogHouse = new Container(new SuperDog("Wonder Dog"))
makeDogsSpeak(superDogHouse)
  1. 解决方案1优于解决方案2有什么优势吗?或者它是一个 偏好问题?
  2. 对于解决方案1,我将代码修改为
  3. class Container[T](val elem : T) // remove covariant from type       
    val superDogHouse = new Container(new SuperDog("Wonder Dog")) 
    makeDogsSpeak(superDogHouse) // compilation error
    

    但如果我这样做,

    makeDogsSpeak(new Container(new SuperDog("Wonder Dog"))) // superDogHouse removed
    

    编译器编译。这里发生了什么?它不是“相同”的代码吗?感谢

1 个答案:

答案 0 :(得分:2)

关于差异声明(问题1 ):

宣言方差异:

class Container[+T](val elem : T)

使用方差异:

def makeDogsSpeak(dogHouse: Container[_ <: Dog])

使用与您的代码设计相关的声明或使用方差异表示法。在某些情况下,方法可以是通用的(并且可以使用简短表示法的类型推断),但在某些情况下,方法不能是通用的。

对于某些类,参数的参数只是良好设计的一种方法。例如:

trait List[+A]
object Nil extends List[Nothing]

使用:

val xs : List[Int] = Nil // will not possible in case List[A]

也适用于某些情况下的声明组合使用side varince,因为容器中参数的位置与方差有关。

协变位置参数:

trait C[+T] {
  def get : T
}

逆变位置:

trait C[-T] {
  def set(x : T): Unit
}

但不是:

trait C[+T] {
  def get : T 
  def set(x : T): Unit // wrong position of type T
}

通过混合声明修复 - &amp;使用方差异:

trait C[+T] {
  def get : T 
  def set[TT >: T](x : TT): Unit // ok
}

关于问题2

考虑类型推断的规则,编译器的代码如下:

val superDogHouse = new Container[SuperDog](new SuperDog("Wonder Dog")) 
makeDogsSpeak(superDogHouse) // compilation error

makeDogsSpeak(new Container[Dog](new SuperDog("Wonder Dog")))

在第一种情况下工作并且在第二种情况下没有工作的原因是一种容器。