之间有什么区别
[A <: B]
和
[+B]
在斯卡拉吗?
Answers:
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
(covariance; Q
covaries-以及-'subclasses B
'继承关系)正确的做法。
我想通过更多示例来扩展Rex Kerr的出色答案:假设我们有四个类:
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不在乎它是否包含Animals或Cars。List的开发人员没有强制要求,例如,只有Cars可以进入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]
参数,则也可以将aList[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是仅用于动物的集合。禁止车辆进入。
Animal
- Dog
/ Car
-SportsCar
类始终是最佳示例。这是非常难以遵循所有这些Q
,A
,B
秒。
List[Car]
我们当然可以添加SportsCar
,即使它被宣布为 List[B]
。如果我们具有like函数def smth(x:MyList[Car]):Unit = {}
,则传递List[SportsCar]
将失败List[B]
,但不会失败List[+B]
,因为现在List[SportsCar]
是的子类List[Car]
,并且可以传递子类代替原始类。
我在研究此问题时找到了此博客文章。对Scala方差进行了更深入的解释,包括类别理论中的理论基础
http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/