代码之家  ›  专栏  ›  技术社区  ›  Julian

在scala中使用协方差符号或泛型边界时

  •  11
  • Julian  · 技术社区  · 14 年前

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

    class List[+A]
    

    所以有协变列表的函数可以这样定义:

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

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

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

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

    所以我的问题是:

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

    THX提前)

    2 回复  |  直到 14 年前
        1
  •  14
  •   Martin Odersky    14 年前

    如果一个类型是自然协变或逆变的,您应该声明它是协变或逆变的。您的用户会为此感谢您。由于Java的原因,使用站点的差异很大程度上是存在的。更准确地说,是一种 Array[T <: Number] 被视为存在类型的简写:

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

    存在类型在scala中有一个相当庞大的语法。这是故意的,因为我们不建议你经常使用它们。你什么时候需要存在主义的类型?

    1. 用通配符编写Java类型的模拟,如 List<? extends Number> .
    2. 编写Java原始类型的模拟,如 List .

    在爪哇中,原始类型和通配符类型并不完全相同,它们既不与存在类型完全相同(即使我们知道它们不是什么,也很难精确地描述它们是什么)。但它们在实践中已经足够接近存在主义者了,因此scala可以将它们映射到这种类型。

        2
  •  6
  •   michid    14 年前
    1. 当创建一个新的泛型类型时,比如foo[t]时,您应该努力确定该类型是协变的、逆变的还是不变的,并分别声明它foo[+t]、foo[-t]或foo[t]。诚然,这可能有点困难。但是,它让foo的用户在每次需要使用foo时都可以使用通用边界来进行决策。简言之:当差异是类型本身的属性时,首选声明站点差异而不是调用站点差异。

    顺便说一句,MartinOdersky、LexSpoon和BillVenners在scala书中的编程对方差有很大的影响。参见第19章类型参数化。