Chinaunix首页 | 论坛 | 博客
  • 博客访问: 184716
  • 博文数量: 29
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 601
  • 用 户 组: 普通用户
  • 注册时间: 2013-07-03 18:51
个人简介

大数据算法,分布式技术,spark技术爱好者

文章分类

全部博文(29)

文章存档

2015年(4)

2014年(3)

2013年(22)

分类: 云计算

2014-12-20 10:49:26

因为Function的泛型里定义了函数入参和出参分别是“逆变”“协变”的:

trait Function1[-T1, +R] {…}


所以A=>B这样的函数类型,也可以有继承关系的。
我们做个测试,先简单些,只看出参类型的(协变容易理解些),A=>B和A=>C两个函数类型;
如果C extends B则A=>C是A=>B的子类型

//定义A=>C类型的函数
scala> val t2 = (p:A)=>new C
 
//可以把 A=>C类型的函数赋值给 A=>B类型的
scala> val t3:A=>B = t2
 
//或直接把t2造型为 A=>B
scala> t2.asInstanceOf[A=>B]

?

再看看入参类型,这个是逆变,继承关系正好相反。
假设: X=>R,Y=>R 如果 Y extends X则 X=>R 是 Y=>R 的子类型

?
//定义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 >

二、classTag 存储的信息如何利用:

直接的泛型T和T:classTag,后者是隐式转换来的,如何区分使用?
classTag类型中包含着更多的类信息,可以在对象的运行期利用类的信息。
比方说新建Array[T](),则必须用classTag, 因为Array用到了T的反射创建Array中的元素。
这些检测都是在编译期,写错没有关系,编译是无法通过的。

new 一个对象是不需要classTag的。但是要调用带参数的构造函数显然是需要的。


一般的如果代码中有ClassOf[T].getClass ....之类的调用是需要classTag的。
如果classTag不够用,更多多信息可以用TypeTag来获得。

下面的文章介绍更加详细:http://fair-jm.iteye.com/blog/2163746


阅读(12098) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~