scala - 为什么像 "cons[B >: A](v: B)"这样定义的方法接受不是 A 父类(super class)型的类型参数?

标签 scala generics covariance contravariance generic-method

我现在正在研究 Scala 中的方差,我想我对逆变有很好的理解。例如给定 trait List[-A] ,我知道List[Int]List[AnyVal] 的父类(super class)型.

但是说我有以下特点:

trait List[+A] {
  def cons(hd: A): List[A]
}

为什么是 cons参数类型错误?

为什么一定要有def cons[B >: A](v: B): List[B] ?

例如:
val animal_list: List[Animal] = List(tiger, dog)

如果我们调用:
animal_list.cons(tiger)

Tiger <: Animal , 没有 cons遇到问题? BTigerAAnimalB >: A不是真的。

最佳答案

为什么是 cons的参数类型不对?

trait List[+A] {
  def cons(hd: A): List[A]
}

编译器给你错误:covariant type A occurs in contravariant position in type A of value hd 因为方法参数算作逆变位置,但是 A是协变的。

让我们想象一下这个方法声明会编译。然后我们可以这样做:
class ListImpl[A] extends List[A] {
  override def cons(hd: A): List[A] = ???
}

val strings: List[String] = new ListImpl[String]
val values: List[Any] = strings // OK, since List[String] <: List[Any] (in List[A], A is covariant)
values.cons(13) // OK(??), since values's static type is List[Any], so argument of cons should be Any, and 13 conforms to type Any

上面最后一行真的好吗?我们正在调用 consvalues . valuesstrings 相同, 和 stringsListImpl[String] 类型的对象.所以cons最后一行中的调用正在等待 String争论,但我们正在通过 Int , 因为 values的静态类型是 List[Any]Int符合 Any .这里肯定有问题 - 应该归咎于哪条线?答案是:cons方法声明。为了解决这个问题,我们必须删除协变类型参数 A从逆变位置(在 cons 声明中)。或者,我们可以制作 A非协变。

另请参阅以下问题:#1 , #2 .

... 没有 cons遇到问题?
trait List[+A] {
  def cons[B >: A](v: B): List[B]
}

val animal_list: List[Animal] = List(tiger, dog)  // We are assuming that List.apply and concrete implementation of List is somewhere defined.

不,animal_list.cons(tiger)调用是类型正确的。

我假设 AnimalDog 的常见父类(super class)型和 Tiger ,以及 dogtigerDog 的实例和 Tiger分别。

animal_list.cons(tiger)调用,两者 AB类型参数实例化为 Animal , 所以 cons方法采用以下形式:
def cons[Animal >: Animal](v: Animal): List[Animal]
Animal >: Animal满足约束是因为:

Supertype and subtype relationships are reflexive, which means a type is both a supertype and a subtype of itself. [source]


cons 的参数是 Tiger符合类型Animal ,所以方法调用是类型正确的。

请注意,如果您强制 B要实例化为 Tiger , 喜欢 animal_list.cons[Tiger](tiger) ,那么此调用将不会是类型正确的,并且您将收到编译器错误。

查看类似示例 here .

关于scala - 为什么像 "cons[B >: A](v: B)"这样定义的方法接受不是 A 父类(super class)型的类型参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37334674/

相关文章:

scala - 使用可选列更新 Scala Slick 行

c# - Cast uint -> double 无效?

python - 在向量上使用 numpy.cov 会产生 NAN

c# - 与 C# 泛型的协变

scala - 理解抽象类型

scala - 如何在Scala测试中检查 “Either”结果?

scala - Typesafe配置: How to iterate over configuration items

Java 泛型 <?> 的含义

java - 使用泛型的 Hashmap getter setter

java - Kotlin 通用反射子类