Scala集合如何在逆变位置中使用某些变体类型?

时间:2014-06-01 22:41:57

标签: scala scala-collections

考虑一个Scala收集器,例如List或一个特性,例如GenTraversibleGenTraversibleLike。所有这些都是使用A类型定义的,因此它是变体(例如List[+A])。

这些类型/特征具有某些方法,例如contains(elem: A)filter(pred: A => Boolean)

我不明白为什么允许这样做 - 似乎类型A出现在逆变位置,即使类/特征声明显示+A(变体)。

2 个答案:

答案 0 :(得分:4)

A处于containsfilter的协变位置。

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现在要求A1Y的超类型,这实际上是一个更严格的要求。 filter现在需要一个函数Y => Boolean,这也是一个更严格的要求,因为传入的函数必须能够处理任何Y,而不仅仅是那些{{1}的实例}。另一方面,将X投射到List[Y]不是类型安全的。如果List[X]也是子类型XZ没有,Y用于Z A1,那么类型绑定将被违反,因为contains不是真的。对于过滤器,传递的函数将为Z >: Y,并且无法安全地传递X => Boolean包含的Y实例。因此,我们可以得出结论:List确实在这些方法中具有协变性而非逆变性。

答案 1 :(得分:1)

这是允许的,因为对于协变类型,带参数的方法可以具有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)
}