11

在 Scala 中,可以使用泛型类型参数上的 + 和 - 等方差运算符来定义方差。例如,List类型在标准库中是协变的。

class List[+A]

因此,可以像这样定义具有协变列表的函数:

def foo[A](list : List[A])

方差也可以用通用边界来模拟。所以我们也可以这样写

def foo[A](list : List[_:< A])

当然这没有意义,因为list已经是协变的。但同样的技巧也可以用于非协变的类型。(如Stack)。当然,也可以从协变的堆栈(聚合的继承)创建新类型。

所以我的问题:

  1. 什么时候应该对方差使用通用界限?我们什么时候应该创建一个新的协变类型?
  2. 通用边界是否仅对方差有用,或者它们可以声明更多(语言概念)。
  3. 如果它们仅对方差有用,那么边界是否仅用于与 Java 兼容?

提前谢谢:)

4

2 回答 2

14

如果一个类型是自然协变或逆变的,你应该这样声明它。您的用户会为此感谢您。使用站点的差异确实主要是因为 Java。更准确地说,诸如此类的类型Array[T <: Number]被视为存在类型的简写:

ArrayBuffer[T] forSome { type T <: Number }

存在类型在 Scala 中有相当庞大的语法。这是故意的,因为我们不建议您过多使用它们。什么时候需要存在类型?

  1. 使用通配符编写 Java 类型的类似物,例如List<? extends Number>.
  2. 编写 Java 原始类型的类似物,例如List.

在 Java 中,原始类型和通配符类型并不完全相同,也不完全与存在类型完全相同(即使我们知道它们不是什么,也很难准确地说明它们是什么)。但它们在实践中与存在主义足够接近,因此 Scala 成功地将它们映射到这种类型。

于 2010-09-08T14:16:41.333 回答
6
  1. 当创建一个新的泛型类型时,比如说 Foo[T],你应该努力确定该类型是协变的、逆变的还是不变的,并分别声明它为 Foo[+T]、Foo[-T] 或 Foo[T]。诚然,这可能有点困难。但是,它使 Foo 的用户不必在每次需要使用 Foo 时通过使用通用边界来做出该决定。简而言之:当方差是类型本身的属性时,更喜欢声明站点方差而不是调用站点方差。

顺便说一句,Martin Odersky、Lex Spoon 和 Bill Venners 合着的 Programming in Scala 书中有一些关于方差的精彩章节。请参阅第 19 章类型参数化。

于 2010-09-08T08:42:00.707 回答