Scala 协变容器

标签 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,我将代码修改为,

.

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):

声明方差异:

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]

此外,在某些情况下,需要混合声明和使用侧面方差,因为容器中参数的位置与方差相关。

协变位置参数:

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
}

通过混合声明和使用方差异来修复:

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")))

它在第一种情况下起作用而在第二种情况下不起作用的原因是容器的类型。

关于Scala 协变容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24161491/

相关文章:

scala - 如何将元素排入可迭代队列,例如队列[列表[节点]]?

scala - 协变类型 T 出现在不变位置

inheritance - 我真的不明白这个co/contravariance的事情...我不能同时拥有通用的get和set方法吗?

c# - C#.NET 中泛型接口(interface)的协变

Scala 多维数组相当于 np.ndarray.shape?

java - 我正在寻找一些帮助,以使用 Play 框架将 Polymer 与服务器端 Scala/java 结合使用

scala - 如何在Scala中实现默认操作的Map

scala - 为什么SBT的线程上下文类加载器不能加载JDK类文件作为资源?

c# - 为什么 List<T> 在协变接口(interface) MyInterface<out T> 上无效

c# - 如何解决 IReadOnlyDictionary 缺乏协方差的问题?