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() {}} |
2 逆变
123456 | class Super{ void doSomething(String parameter)}class Sub extends Super{ void doSomething(Object parameter)} |
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-)。我们可以使用数学概念(partialorder)来定义:
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) = {}} |
但是如果返回结果为类型参数则没有问题。
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,否则上面的代码会编译出错:
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) |
相关文章推荐
- Windows下Scala环境搭建
- Windows7下安装Scala 2.9.2教程
- Scala代码实现列出Hadoop 文件夹下面的所有文件
- ClassNotFoundException:scala.PreDef$
- sbt创建web项目
- XML 文件解析--含Unicode字符的XML文件
- Scala 学习随笔
- Scala 小程序记录(学习期间的代码片段)
- Spark机器学习(二) 局部向量 Local-- Data Types - MLlib
- Spark机器学习(三) Labeled point-- Data Types
- 分分钟掌握快速排序(Java / Scala 实现)
- Scala极速入门
- Spark初探
- Scala实现REST操作
- Scala method call syntax
- 关于Scala多重继承的菱形问题
- Scala 高阶函数(high-order function)剖析
- Scala Monad Design Pattern
- Spray.io搭建Rest服务
- Spray.io搭建Rest — 支持Twirl模板并部署