Scala中的协变,逆变,上界,下界等
2017-03-30 10:20
232 查看
Java中的协变和逆变
首先我们先回顾一下Java中的协变和逆变,这样我们更容易理解Scala中的协变和逆变。1 协变
class Super { Object getSomething(){} } class Sub extends Super { String getSomething() {} }
Sub.getSomething()是一个协变类型,因为它的返回类型是Super.getSomething返回类型的子类。
2 逆变
class Super{ void doSomething(String parameter) } class Sub extends Super{ void doSomething(Object parameter) }
Sub.getSomething()是一个逆变类型,因为它的输入参数是Super.getSomething输入参数的父类。
3 泛型
泛型中也有协变和逆变。
List<String> aList... List<? extends Object> covariantList = aList; List<? super String> contravariantList = aList; covariantList.add("d"); //wrong Object a = covariantList.get(0); contravariantList.add("d"); //OK String b = contravariantList.get(1); //wrong Object c = contravariantList.get(2);
你可以调用
covariantList所有的不需要泛型参数的方法,因为泛型参数必须 extends
Object, 但是编译时你不知道它确切的类型。但是你可以调用getter方法,因为返回类型总是符合Object类型。
contravariantList正好相反,你可以调用所有的带泛型参数的方法,因为你明确的可以传入一个String的父类。但是getter方法却不行。
Scala的协变
首先我们需要了解的是子类型(subtyping)。一个类可以是其它类的子类(sub-)或者父类(super-)。我们可以使用数学概念(partialorder)来定义:
A -> B iff A <: B
当我们定义一个协变类型
List[A+]时,List[Child]可以是List[Parent]的子类型。
当我们定义一个逆变类型
List[-A]时,List[Child]可以是List[Parent]的父类型。
看下面的例子:
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定义成不变类型。
稍微改一下:
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的逆变
将上面的例子改一下: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
如果协变类包含带类型参数的方法时:class Consumer[+T](t: T) { def use(t: T) = {} }
编译会出错。出错信息为 "Covariant type T occurs in contravariant position in type T of value t"。
但是如果返回结果为类型参数则没有问题。
class Consumer[+T](t: T)(implicit m1:Manifest[T]) { def get(): T = {m1.runtimeClass.newInstance.asInstanceOf[T]} }
为了在方法的参数中使用类型参数,你需要定义下界:
class Consumer[+T](t: T) { def use[U >: T](u : U) = {println(u)} }
上界upper bounds
逆变类中使用上界的例子:class Consumer[-T](t: T) { def get[U <: T]()(implicit m1:Manifest[U]): U = {m1.runtimeClass.newInstance.asInstanceOf[U]} }
可以看到方法的返回值是协变类型,方法的参数是逆变类型。
因此协变类的类型参数可以用在方法的返回值的类型,在方法的参数类型上必须使用下界绑定
>:。
逆变类的类型参数可以用在方法的参数类型上,用做方法的返回值类型时必须使用上界绑定
<:。
综合协变,逆变,上界,下界
一个综合例子:class Animal {} class Bird extends Animal {} class Consumer[-S,+T]()(implicit m1:Manifest[T]) { def m1[U >: T](u: U): T = {m1.runtimeClass.newInstance.asInstanceOf[T]} //协变,下界 def m2[U <: S](s: S)(implicit m2:Manifest[U]): U = {m1.runtimeClass.newInstance.asInstanceOf[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还有一种视图绑定的功能,如class Bird {def sing = {}} class Toy {} class Consumer[T <% Bird]() { def use(t: T) = t.sing }
或者类型参数在方法上:
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,否则上面的代码会编译出错:
No implicit view available from Toy => Bird.
加入一个隐式转换,编译通过。
import scala.language.implicitConversions class 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],语法如下:
def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]
更清晰的一个例子:
def f[A : ClassManifest](n: Int) = new Array[A](n)
又比如
def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)
相关文章推荐
- (转)Scala中协变(+)、逆变(-)、上界(<:)、下界(>:)简单介绍
- Scala类型参数中协变(+)、逆变(-)、类型上界(<:)和类型下界(>:)的使用
- scala-协变、逆变、上界、下界
- Programming In Scala笔记-第十九章、类型参数,协变逆变,上界下界
- Scala中的协变,逆变,非变,上界,下界
- Scala中的协变,逆变,上界,下界等
- Scala类型参数中协变(+)、逆变(-)、类型上界(<:)和类型下界(>:)的使用
- scala-协变、逆变、上界、下界
- Scala进阶27-List中的上界、下界、逆变、协变
- Scala类型参数中协变(+)、逆变(-)、类型上界(<:)和类型下界(>:)的使用
- scala的协变逆变,上界下界
- Scala中的协变,逆变,上界,下界等
- scala的协变与逆变、上界与下界
- Scala类型参数中协变(+)、逆变(-)、类型上界(<:)和类型下界(>:)的使用
- scala-协变、逆变、上界、下界
- Scala的协变covariant(+),逆变contravariant(-),上界(<:),下界(>:)
- Scala List中的上界下界以及逆变、协变
- scala学习笔记-类型参数中协变(+)、逆变(-)、类型上界(<:)和类型下界(>:)的使用
- scala-协变、逆变、上界、下界
- scala-协变、逆变、上界、下界