2010-09-08 61 views
11

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

class List[+A] 

因此,与协变列表中的函数可以这样定义:

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

方差也可以用普通的界限进行仿真。因此,我们也可以这样写

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

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

所以我的问题:

  1. 时,应使用通用的界限进行方差?何时应该创建一个新的协变类型?
  2. 泛型边界仅对方差有用,还是可以声明更多(语言概念)。
  3. 如果它们只对方差有用,是否仅限于兼容Java?

提前:)

+1

我将这个问题加倍 - 差异是Scala中最难处理的部分,我实际上对它的理解并不理想。 “Blah-blah covariant blah出现在逆变位置blah-blah”:p – Jeriho 2010-09-08 07:55:43

+2

当我观察一些函数特征(函数1,函数2等)的定义时,我理解了变化函数特征都是输出类型的协变并且与输入(参数)类型相反。例如,任何类型为String => Unit的函数都可以使用类型为“Any => Unit”的函数,因为“Any => Unit”函数可以接受一个字符串作为输入(因为它可以以任何东西为输入。)这就是为什么方法参数的类型是“逆变位置”。 – 2010-09-08 12:18:02

回答

13

如果某种类型自然是协变或逆变的,那么应该声明它。你的用户会为此感谢你。由于Java,使用地点差异确实大部分存在。更确切地说,如Array[T <: Number]类型被视为速记的存在类型:

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

生存类型有Scala中的一个非常庞大的语法。这是有意的,因为我们不建议你多用它们。你什么时候需要存在类型?

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

在Java中,原始类型和通配符类型不完全相同,它们与存在类型都不完全相同(即使我们知道它们不是什么,也很难确切地说明它们是什么)。但是它们在实践中已经足够接近实践,所以Scala可以将它们映射到这种类型。

6
  1. 当创建一个新的通用型,说富[T],你应该努力以确定类型是协变,逆变或不变,并宣布它的Foo [THX + T],Foo [-T]或Foo [T]。无可否认,这可能有点困难。但是,每当她需要使用Foo使用泛型界限时,它就释放了Foo的用户做出该决定的用户。简而言之:当差异是类型本身的一个属性时,更希望声明站点差异超过呼叫站点差异。

顺便说一句,Martin Odersky,Lex Spoon和Bill Venners编写的Scala书籍中有关于方差的一些重要反应。请参见第19章类型参数化。