在Scala中使用协方差符号或通用边界时

Jul*_*ian 11 scala variance bounds type-bounds

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

class List[+A]
Run Code Online (Sandbox Code Playgroud)

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

def foo[A](list : List[A])
Run Code Online (Sandbox Code Playgroud)

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

def foo[A](list : List[_:< A])
Run Code Online (Sandbox Code Playgroud)

当然这没有道理,因为list它已经是协变的.但是对于不协变的类型也可以采用相同的技巧.(像Stack).当然,也可以从协同的堆栈(聚合继承)创建新类型.

所以我的问题:

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

thx提前:)

Mar*_*sky 14

如果一个类型是自然的协变或逆变,你应该声明它.您的用户会感谢您的支持.由于Java,使用站点方差确实存在.更准确地说,类型如Array[T <: Number]被视为存在类型的简写:

ArrayBuffer[T] forSome { type T <: Number }
Run Code Online (Sandbox Code Playgroud)

存在类型在Scala中具有相当庞大的语法.这有点故意,因为我们不建议你多用它们.你什么时候需要一个存在主义类型?

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

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


mic*_*hid 6

  1. 在创建一个新的泛型类型时,比如说Foo [T],你应该努力确定该类型是协变的,逆变的还是不变的,并分别声明它为Foo [+ T],Foo [-T]或Foo [T].不可否认,这可能有点困难.但是,每当她需要通过使用泛型边界来使用Foo时,它就会让Foo的用户自由地做出决定.简而言之:当方差是类型本身的属性时,更喜欢声明站点差异而不是呼叫站点方差.

BTW,Martin Odersky编写的Scala编程书,Lex Spoon和Bill Venners有一些关于方差的很好的选择.请参见第19章类型参数化.