Scala中A<:B和+ B之间有什么区别?

时间:2010-12-25 19:49:58

标签: scala variance

之间有什么区别
[A <: B]

[+B]
在Scala中

4 个答案:

答案 0 :(得分:164)

Q[A <: B]表示类Q可以接受A的子类B

Q[+B]表示Q可以使用任何类,但如果AB的子类,那么Q[A]被认为是Q[B]的子类。

Q[+A <: B]表示类Q只能获取B的子类以及传播子类关系。

当你想要做一些通用的事情时,第一个很有用,但你需要依赖B中的某组方法。例如,如果您的Output类具有toFile方法,则可以在任何可以传递到Q的类中使用该方法。

当您想要使集合的行为与原始类相同时,第二个非常有用。如果您使用B并创建了子类A,则可以在A的任何地方传递B。但是,如果您使用B Q[B]集合,那么您是否可以始终传入Q[A]?一般来说,没有;有些情况下这是错误的做法。但是你可以说使用+B(协方差; Q covaries - 跟随 - B的子类'继承关系',这是正确的做法。

答案 1 :(得分:42)

我想用更多示例扩展Rex Kerr's excellent answer: 假设我们有四个类:

 class Animal {}
 class Dog extends Animal {}

 class Car {}
 class SportsCar extends Car {}

让我们从方差开始:

 case class List[+B](elements: B*) {} // simplification; covariance like in original List

 val animals: List[Animal] = List( new Dog(), new Animal() )
 val cars: List[Car] = List ( new Car(), new SportsCar() )

如您所见列表不关心它是否包含动物或汽车。 List的开发人员没有强制执行此操作,例如只有汽车可以进入列表。

另外:

case class Shelter(animals: List[Animal]) {}

val animalShelter: Shelter = Shelter( List(new Animal()): List[Animal] )
val dogShelter: Shelter = Shelter( List(new Dog()): List[Dog] )

如果函数需要List[Animal]参数,您也可以将List[Dog]作为参数传递给函数。由于List的协方差,List[Dog] 被视为 List[Animal]的子类。如果List是不变的,它将无法工作。

现在进入类型边界:

case class Barn[A <: Animal](animals: A*) {}

val animalBarn: Barn[Animal] = Barn( new Dog(), new Animal() )
val carBarn = Barn( new SportsCar() )
/* 
error: inferred type arguments [SportsCar] do not conform to method apply's type parameter bounds [A <: Animal]
    val carBarn = Barn(new SportsCar())
                 ^
*/

如您所见 Barn是仅适用于动物的集合。这里没有车。

答案 2 :(得分:2)

我的理解:


第一个是参数类型绑定,在我们的例子中有一个上限和下限类型,它是一个“类型参数A,它是B的一个子类型(或B本身)。


第二个是类定义的方差注释,在我们的例子中是B的协方差子类


Scala:+ Java :?扩展T Covariant子类化

Scala: - Java :? super T Contravariant子类化

答案 3 :(得分:2)

我在研究这个问题时发现了这篇博文。对Scala方差进行更深入的解释,包括其在类别理论中的理论基础

http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/