您的位置:首页 > 其它

Scala中的协变,逆变,上界,下界等

2016-06-07 20:55 447 查看


目录 [−]

Java中的协变和逆变

Scala的协变

Scala的逆变

下界lower
bounds

上界upper
bounds

综合协变,逆变,上界,下界

View
Bound <%

Context
Bound

参考文档

Scala中的协变逆变和Java中的协变逆变不一样,看起来更复杂。 本文对Scala中的这些概念做一总结。

首先看几个概念:

covariant 协变。使你能够使用比原始指定的类型的子类

Contravariance 逆变。使你能够使用比原始指定的类型的父类。

Invariance 不变。你只能使用原始指定的类型,不能协变和逆变

Upper bounds 上界。

Lower bounds 下界。


Java中的协变和逆变

首先我们先回顾一下Java中的协变和逆变,这样我们更容易理解Scala中的协变和逆变。

1 协变
123456

class Super {  Object getSomething(){}}class Sub extends Super {  String getSomething() {}}

Sub.getSomething()是一个协变类型,因为它的返回类型是Super.getSomething返回类型的子类。

2 逆变
123456

class Super{  void doSomething(String parameter)}class Sub extends Super{  void doSomething(Object parameter)}

Sub.getSomething()是一个逆变类型,因为它的输入参数是Super.getSomething输入参数的父类。

3 泛型

泛型中也有协变和逆变。
12345678910

List<String> aList...List<? extends Object> covariantList = aList;List<? super String> contravariantList = aList;covariantList.add("d"); //wrongObject a = covariantList.get(0);		contravariantList.add("d"); //OKString b = contravariantList.get(1); //wrongObject c = contravariantList.get(2);

你可以调用
covariantList
所有的不需要泛型参数的方法,因为泛型参数必须 extends
Object, 但是编译时你不知道它确切的类型。但是你可以调用getter方法,因为返回类型总是符合Object类型。

contravariantList
正好相反,你可以调用所有的带泛型参数的方法,因为你明确的可以传入一个String的父类。但是getter方法却不行。


Scala的协变

首先我们需要了解的是子类型(subtyping)。一个类可以是其它类的子类(sub-)或者父类(super-)。我们可以使用数学概念(partial
order)来定义:
1

A -> B iff A <: B

当我们定义一个协变类型
List[A+]
时,List[Child]可以是List[Parent]的子类型。

当我们定义一个逆变类型
List[-A]
时,List[Child]可以是List[Parent]的父类型。

看下面的例子:
12345678910

class Animal {}class Bird extends Animal {}class Consumer[T](t: T) {}class Test extends App {	val c:Consumer[Bird] = new Consumer[Bird](new Bird)	val c2:Consumer[Animal] = c}

c
不能赋值给
c2
,因为
Consumer
定义成不变类型。

稍微改一下:
12345678910

class Animal {}class Bird extends Animal {}class Consumer[+T](t: T) {}class Test extends App {	val c:Consumer[Bird] = new Consumer[Bird](new Bird)	val c2:Consumer[Animal] = c}

因为
Consumer
定义成协变类型的,所以
Consumer[Bird]
Consumer[Animal]
的子类型,所以它可以被赋值给
c2


Scala的逆变

将上面的例子改一下:
12345678910

class Animal {}class Bird extends Animal {}class Consumer[-T](t: T) {}class Test extends App {	val c:Consumer[Bird] = new Consumer[Bird](new Bird)	val c2:Consumer[Hummingbird] = c}

这里
Consumer[-T]
定义成逆变类型,所以
Consumer[Bird]
被看作
Consumer[Hummingbird]
的子类型,故
c
可以被赋值给
c2


下界lower bounds

如果协变类包含带类型参数的方法时:
123

class Consumer[+T](t: T) { 	def use(t: T) = {}}

编译会出错。出错信息为 "Covariant type T occurs in contravariant position in type T of value t"。

但是如果返回结果为类型参数则没有问题。
123

class Consumer[+T](t: T) { 	def get(): T = {new T}}

为了在方法的参数中使用类型参数,你需要定义下界:
123

class Consumer[+T](t: T) {	def use[U >: T](u : U) = {println(u)}}


上界upper bounds

逆变类中使用上界的例子:
123

class Consumer[-T](t: T) {	def get[U <: T](): U = {new U}}

可以看到方法的返回值是协变类型,方法的参数是逆变类型。

因此协变类的类型参数可以用在方法的返回值的类型,在方法的参数类型上必须使用下界绑定
>:


逆变类的类型参数可以用在方法的参数类型上,用做方法的返回值类型时必须使用上界绑定
<:


综合协变,逆变,上界,下界

一个综合例子:
1234567891011121314

class Animal {}class Bird extends Animal {}class Consumer[-S,+T]() {	def m1[U >: T](u: U): T = {new T} //协变,下界	def m2[U <: S](s: S): U = {new U} //逆变,上界}class Test extends App {	val c:Consumer[Animal,Bird] = new Consumer[Animal,Bird]()	val c2:Consumer[Bird,Animal] = c	c2.m1(new Animal)	c2.m2(new Bird)}


View Bound
<%

Scala还有一种视图绑定的功能,如
123456

class Bird {def sing = {}}class Toy {}class Consumer[T <% Bird]() {	def use(t: T) = t.sing}

或者类型参数在方法上:
1234567891011

class Bird {def sing = {}}class Toy {}class Consumer() {	def use[T <% Bird](t: T) = t.sing}class Test extends App {	val c = new Consumer()	c.use(new Toy)}

它要求T必须有一种隐式转换能转换成Bird,也就是
T
=> Bird
,否则上面的代码会编译出错:
1

No implicit view available from Toy => Bird.

加入一个隐式转换,编译通过。
1234567891011121314

import scala.language.implicitConversionsclass Bird {def sing = {}}class Toy {}class Consumer() {	def use[T <% Bird](t: T) = t.sing}class Test extends App {	implicit def toy2Bird(t: Toy) = new Bird	val c = new Consumer()	c.use(new Toy)}


Context Bound

context bound在Scala 2.8.0中引入,也被称作type class pattern。

view bound使用
A
<% String
方式,context bound则需要参数化的类型,如
Ordered[A]


它声明了一个类型
A
,隐式地有一个类型
B[A]
,语法如下:
1

def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]

更清晰的一个例子:
1

def f[A : ClassManifest](n: Int) = new Array[A](n)

又比如
1

def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  scala