我现在正在研究 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
遇到问题? 自 B
是 Tiger
和 A
是 Animal
和 B >: 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
上面最后一行真的好吗?我们正在调用
cons
在 values
. values
与 strings
相同, 和 strings
是 ListImpl[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)
调用是类型正确的。 我假设
Animal
是 Dog
的常见父类(super class)型和 Tiger
,以及 dog
和 tiger
是 Dog
的实例和 Tiger
分别。在
animal_list.cons(tiger)
调用,两者 A
和 B
类型参数实例化为 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/