[英]How do Scala collections use some variant types in contravariant position?
考虑一个Scala GenTraversible
例如List
或一个特征(例如GenTraversible
和GenTraversibleLike
。 所有这些都用类型A
定义,以便它是变量(例如List[+A]
)。
这些类型/特征具有某些方法,例如contains(elem: A)
和filter(pred: A => Boolean)
。
我不明白为什么允许这样做-即使类别/特征声明为+A
(变体),似乎A
型也出现在变数位置。
对于contains
和filter
A
处于协变位置。
contains
的签名实际上是:
def contains[A1 >: A](elem: A1): Boolean
当在下类型边界的右侧使用类型变量时,它处于协变位置。
使用filter
,参数的类型为A => Boolean
,即Function1[A, Boolean]
。 Function1
的第一个参数是互变量的,而Function1
本身作为要filter
的参数处于互变的位置。 两个逆方差组合在一起构成协方差。
解决这个问题的一种方法是给List[X]
成像,其中X <: Y
。 如果将此List[X]
强制转换为List[Y]
,这些方法是否仍然是类型安全的? 现在contains
要求A1
是Y
的超类型,这实际上是更严格的要求。 filter
现在需要一个函数Y => Boolean
,这也是一个更严格的要求,因为传入的函数必须能够处理任何Y
,而不仅仅是X
实例。 另一方面,将List[Y]
强制转换为List[Y]
List[X]
将不是类型安全的。 如果X
也是Z
子类型,但Y
没有,并且Z
被用于contains
A1
,那么将违反类型绑定,因为Z >: Y
不是真的。 对于filter,传递的函数将是X => Boolean
,并且不能安全地传递List
包含的Y
的实例。 因此,我们可以得出结论,在这些方法中, A
确实处于协变而非协变的位置。
允许这样做是因为对于协变类型,采用参数的方法的LOWER BOUND可以为T。请考虑以下示例:
trait myList[+T] {
def prepend(elem: T): List[T] = new Cons(elem, this)
}
这将不起作用,因为您不能将协变类型作为参数-您将获得编译时错误。 另一方面,您可以将方法参数限制为T的下限-参数可以是T或T的超类型,而不是子类型,例如
trait myList[+T] {
def prepend[S >: T](elem: S): List[S] = new Cons(elem, this)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.