我现在正在研究scala中的方差,我认为我对反方差有很好的理解。例如,给定trait List[-A]
,我知道List[Int]
是List[AnyVal]
的超类型。
但是说我有以下特点:
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
上面最后一行真的可以吗?我们在values
上呼叫cons
。values
与strings
相同,并且strings
是类型为ListImpl[String]
的对象。因此,最后一行中的cons
调用需要String
参数,但我们传递的是Int
,因为values
的静态类型是List[Any]
,而Int
符合Any
。这里肯定出了问题——该怪哪一行?答案是:cons
方法声明。为了解决这个问题,我们必须从逆变位置删除协变类型参数A
(在cons
声明中)。或者,我们可以使CCD_ 32非协变。
另请参阅以下问题:#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
和Tiger
的公共超类型,dog
和tiger
分别是Dog
和Tiger
的实例。
在animal_list.cons(tiger)
调用中,A
和B
类型参数都实例化为Animal
,因此cons
方法的形式为:
def cons[Animal >: Animal](v: Animal): List[Animal]
满足Animal >: Animal
约束是因为:
超类型和子类型关系是自反的,这意味着类型既是其自身的超类型,也是其自身的子类型。[来源]
cons
的参数是Tiger
,它符合类型Animal
,因此方法调用的类型正确。
请注意,如果强制将B
实例化为Tiger
,就像animal_list.cons[Tiger](tiger)
一样,那么此调用的类型将不正确,并且会出现编译器错误。
请参阅此处的类似示例。