http://hongjiang.info/scala-function-polymorphic/
因为Function的泛型里定义了函数入参和出参分别是“逆变”和“协变”的:
|
1
|
trait Function1[-T1, +R] {…} |
所以A=>B这样的函数类型,也可以有继承关系的。
我们做个测试,先简单些,只看出参类型的(协变容易理解些),A=>B和A=>C两个函数类型;
如果C extends B则A=>C是A=>B的子类型
|
1
2
3
4
5
6
7
8
9
10
|
scala> class A; class B; class C extends B//定义A=>C类型的函数scala> val t2 = (p:A)=>new C//可以把 A=>C类型的函数赋值给 A=>B类型的scala> val t3:A=>B = t2//或直接把t2造型为 A=>Bscala> t2.asInstanceOf[A=>B] |
再看看入参类型,这个是逆变,继承关系正好相反。
假设: X=>R,Y=>R 如果 Y extends X则 X=>R 是 Y=>R 的子类型
|
1
2
3
4
5
6
7
8
9
10
|
scala> class R; class X; class Y extends X //定义X=>R类型的函数scala> val f1 = (x:X)=>new R//把X=>R类型的函数赋值给 Y=>R 类型的scala> val f2:Y=>R = f1//或直接造型scala> f1.asInstanceOf[Y=>R] |
协变和逆变的场景与java泛型的”PECS原则”一致
注: PECS 是Joshua Bloch在《Effictive Java》里提出的一个原则。
当参数(容器)是一个生产者(producer)提供元素给你的代码来用(即容器只读),那么容器的泛型应该使用:Collection< ? extends T >
当参数(容器)作为一个消费者(consumer)来消费你提供的数据(即容器可写),那么容器的泛型应该使用:Collection< ? super T >
http://blog.csdn.net/li385805776/article/details/19201615
Scala有秩1多态性
粗略地说,这意味着在Scala中,有一些你想表达的类型概念“过于泛化”以至于编译器无法理解。假设你有一个函数
def toList[A](a: A) = List(a)
你希望继续泛型地使用它:
def foo[A, B](f: A => List[A], b: B) = f(b)
这段代码不能编译,因为所有的类型变量只有在调用上下文中才被固定。即使你“钉住”了类型B:
def foo[A](f: A => List[A], i: Int) = f(i)
…你也会得到一个类型不匹配的错误。
def foo[A](f: A => List[A], i: A) = f(i)
这样可以通过,在调用上下文中确定
变性 Variance
Scala的类型系统必须同时解释类层次和多态性。类层次结构可以表达子类关系。在混合OO和多态性时,一个核心问题是:如果T’是T一个子类,Container[T’]应该被看做是Container[T]的子类吗?变性(Variance)注解允许你表达类层次结构和多态类型之间的关系:
| 含义 | Scala 标记 | |
| 协变covariant | C[T’]是 C[T] 的子类 | [+T] |
| 逆变contravariant | C[T] 是 C[T’]的子类 | [-T] |
| 不变invariant | C[T] 和 C[T’]无关 | [T] |
子类型关系的真正含义:对一个给定的类型T,如果T’是其子类型,你能替换它吗?
scala> class Covariant[+A]
defined class Covariant
scala> val cv: Covariant[AnyRef] = new Covariant[String]
cv: Covariant[AnyRef] = Covariant@4035acf6
scala> val cv: Covariant[String] = new Covariant[AnyRef]
<console>:6: error: type mismatch;
found : Covariant[AnyRef]
required: Covariant[String]
val cv: Covariant[String] = new Covariant[AnyRef]
^
scala> class Contravariant[-A]
defined class Contravariant
scala> val cv: Contravariant[String] = new Contravariant[AnyRef]
cv: Contravariant[AnyRef] = Contravariant@49fa7ba
scala> val fail: Contravariant[AnyRef] = new Contravariant[String]
<console>:6: error: type mismatch;
found : Contravariant[String]
required: Contravariant[AnyRef]
val fail: Contravariant[AnyRef] = new Contravariant[String]
^
逆变似乎很奇怪。什么时候才会用到它呢?令人惊讶的是,函数特质的定义就使用了它!
trait Function1 [-T1, +R] extends AnyRef
如果你仔细从替换的角度思考一下,会发现它是非常合理的。让我们先定义一个简单的类层次结构:
scala> class Animal { val sound = "rustle" }
defined class Animal
scala> class Bird extends Animal { override val sound = "call" }
defined class Bird
scala> class Chicken extends Bird { override val sound = "cluck" }
defined class Chicken
假设你需要一个以Bird为参数的函数:
scala> val getTweet: (Bird => String) = // TODO
标准动物库有一个函数满足了你的需求,但它的参数是Animal。在大多数情况下,如果你说“我需要一个___,我有一个___的子类”是可以的。但是,在函数参数这里是逆变的。如果你需要一个参数为Bird的函数,并且指向一个参数为Chicken的函数,那么给它传入一个Duck时就会出错。但指向一个参数为Animal的函数就是可以的:
scala> val getTweet: (Bird => String) = ((a: Animal) => a.sound ) getTweet: Bird => String = <function1>
函数的返回值类型是协变的。如果你需要一个返回Bird的函数,但指向的函数返回类型是Chicken,这当然是可以的。
scala> val hatch: (() => Bird) = (() => new Chicken ) hatch: () => Bird = <function0>
边界
Scala允许你通过 边界 来限制多态变量。这些边界表达了子类型关系。
scala> def cacophony[T](things: Seq[T]) = things map (_.sound)
<console>:7: error: value sound is not a member of type parameter T
def cacophony[T](things: Seq[T]) = things map (_.sound)
^
scala> def biophony[T <: Animal](things: Seq[T]) = things map (_.sound)
biophony: [T <: Animal](things: Seq[T])Seq[java.lang.String]
scala> biophony(Seq(new Chicken, new Bird))
res5: Seq[java.lang.String] = List(cluck, call)
类型下界也是支持的,这让逆变和巧妙协变的引入得心应手。List[+T]是协变的;一个Bird的列表也是Animal的列表。List定义一个操作::(elem T)返回一个加入了elem的新的List。新的List和原来的列表具有相同的类型:
scala> val flock = List(new Bird, new Bird) flock: List[Bird] = List(Bird@7e1ec70e, Bird@169ea8d2) scala> new Chicken :: flock res53: List[Bird] = List(Chicken@56fbda05, Bird@7e1ec70e, Bird@169ea8d2)
List 同样 定义了::[B >: T](x: B) 来返回一个List[B]。请注意B >: T,这指明了类型B为类型T的超类。这个方法让我们能够做正确地处理在一个List[Bird]前面加一个Animal的操作:
scala> new Animal :: flock res59: List[Animal] = List(Animal@11f8d3a8, Bird@7e1ec70e, Bird@169ea8d2)
注意返回类型是Animal。
浙公网安备 33010602011771号