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

Java中的协变和逆变

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

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

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

逆变

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

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

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

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-)。我们可以使用数学概念(partial order)来定义:

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)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值