之间有什么区别
[A <: B]
和
[+B]
在Scala中?
答案 0 :(得分:164)
Q[A <: B]
表示类Q
可以接受A
的子类B
。
Q[+B]
表示Q
可以使用任何类,但如果A
是B
的子类,那么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/