作者:摆摆少年梦

视频地址:http://blog.csdn.net/wsscy2004/article/details/38440247

本节主要内容

  1. Type Specialization
  2. Manifest、TypeTag、ClassTag
  3. Scala类型系统总结

在scala中,类(class)与类型(type)是两个不一样的概念。我们知道类是对同一类型数据的抽象,而类型则更详细。

比方定义class List[T] {}, 能够有List[Int] 和 List[String]等详细类型。称List为类,而List[Int]、List[String]则为类型。

从这方面看:类型一致的对象它们的类也是一致的。而类一致的,其类型不一定一致。比如:

//List[Int]与List[String]它们的类是同样的即List
scala> classOf[List[Int]] == classOf[List[String]]
res1: Boolean = true scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
//类同样,但它们的类型是不一样的
scala> typeOf[List[Int]] == typeOf[List[String]]
res3: Boolean = false

1. Type Specialization

Type Specialization。一般被翻译成类型专门化,它主要是用来解决泛型的类型擦除和自己主动装箱拆箱的问题。在JAVA语言其中。泛型生成字节码文件时会进行泛型类型擦除,类型擦除后利用上界类型(通常是Object)来替代。但这么做的话有问题。这是由于在Java语言中基本类型与对象类型是不能相互引用的,java中的基本类型不能使用泛型。解决方式是利用相应的对象类型来进行替代,比如int相应Integer类型,但这样的方式并不能解决根本问题。为方便后面Type Specialization的理解,我们先从java的类型擦除、自装箱与拆箱讲起。

1 类型擦除

如果我们利用Java泛型定义了以下的Person类:

//Java泛型类
public class Person<T> {
private T firstName;
private T secondName;
public Person(T firstName,T secondName){
this.firstName=firstName;
this.secondName=secondName;
}
public T getFirstName() {
return firstName;
}
public void setFirstName(T firstName) {
this.firstName = firstName;
}
public T getSecondName() {
return secondName;
}
public void setSecondName(T secondName) {
this.secondName = secondName;
} }

经过类型擦除后,终于变为:

public class Person {
private Object firstName;
private Object secondName;
public Person(Object firstName,Object secondName){
this.firstName=firstName;
this.secondName=secondName;
}
public Object getFirstName() {
return firstName;
}
public void setFirstName(Object firstName) {
this.firstName = firstName;
}
public Object getSecondName() {
return secondName;
}
public void setSecondName(Object secondName) {
this.secondName = secondName;
} }

经过类型擦除后的类称为原始类型,从这点来看,java中的泛型事实上是一个伪泛型,它仅仅在编译层次进行实现。在生成字码码这部分泛型信息被擦除。

以下的样例证明也证明了这一点:

public static void main(String[] args) {
Person<String> p1=new Person<String>("张", "三");
Person<Integer> p2=new Person<Integer>(1, 23);
//以下的代码返回的是true
System.out.println(p1.getClass()==p2.getClass());
}

java中的类型擦除会引起一些问题,详细能够參考http://blog.csdn.net/lonelyroamer/article/details/7868820

2 自己主动装箱与拆箱

在前面给的演示样例代码中。我们直接使用

Person<Integer> p2=new Person<Integer>(1, 23);

须要注意的是这里使用的是java的基本类型进行对象的创建。而给定的详细类型是Integer,此时Java会帮我们自己主动进行转换,这个转换操作被称为自己主动装箱(autoboxing),上面的代码相当于:Person<Integer> p2=new Person<Integer>(Integer.valueOf(1), Integer.valueOf(23));

以下的代码演示了拆箱(unboxing)


//Integer firstName =Integer.valueOf(23)
Integer firstName = 23; //自己主动装箱
//拆箱,实际执行 int name = firstName .intValue();
int name = firstName ;

自己主动装箱与拆箱须要损耗一定的性能。当性能要求较高时须要程序猿手动云进行转换。Scala中的Type Specialization攻克了这些问题。它的语法非常easy,通过注解进行类型专门化声明。如:

//在泛型參数前面加@specialized进行Type Specialization
abstract class List[@specialized T]{
def apply(x:T)
def map[S](f:T=>S)
}

上述代码编译后会生成下列字代码文件。例如以下图

从图中能够看到。共生成了九个版本号的List,其中这九个文件分别相应scala中的九种基本类型即Unit, Boolean, Byte, Short, Char, Int, Long, Float, Double。

感兴趣的能够利用javap命令进行查看,这里给出其Byte类型的实现:

D:\ScalaWorkspace\ScalaChapter24\bin\cn\scala\xtwy\advancedtype>javap -private L
ist$mcB$sp.class
Compiled from "TypeSpecilization.scala"
public abstract class cn.scala.xtwy.advancedtype.List$mcB$sp extends cn.scala.xt
wy.advancedtype.List<java.lang.Object> {
public abstract void apply(byte);
public abstract <S extends java/lang/Object> void map(scala.Function1<java.lan
g.Object, S>);
public cn.scala.xtwy.advancedtype.List$mcB$sp();
}

@specialized 还能够更仔细。限定某个或几个基本类型,比如:

abstract class List[@specialized T]{
//指定生成Int类型的版本号
def apply[@specialized (Int) S](x:S):S
////指定生成Boolean及Double类型的版本号
def map[@specialized (Boolean,Double) S](f:T=>S)
}

在上一讲中我们看到了Function1及Function2的类定义中也使用@specialize进行注解。如:

@annotation.implicitNotFound(msg = "No implicit view available from ${T1} => ${R}.")
trait Function1[@specialized(scala.Int, scala.Long,
scala.Float, scala.Double/*, scala.AnyRef*/) -T1,
scala.Float, scala.Long, scala.Double/*,
scala.AnyRef*/) +R] extends AnyRef

能够看到,Function1类也进行了类型专门化。

2. Manifest、TypeTag、ClassTag

本节内容大多来源于自官方文档http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html,大家在学习的时候。可看打开API文档,对本节内容进行理解。

由于类型擦除的影响。编译期存在的类型信息在编译后不存在了,在程序执行时不能获取该信息。但某些场景下可能须要得到编译期的类型信息。scala能够做到这一点,它通过Manifest和TypeTag来保存类型信息并在执行时使用该信息。那Manifest与TypeTag有什么差别呢?Manifest在scala.reflect包中,它在scala.reflect包中。而TypeTag 在scala.reflect.runtime.universe包中定义;TypeTag能够用来替代Manifest。功能更强大一点。Manifest不能识别路径依赖类型,比如对于class Outter{ class Inner}。如果分别创建了两个不同的外部类,outter.Inner, outter2.Inner, Manifest就会识别为同一类型,而TypeTag不会。另外TypeTag能够使用typeOf[T] 来检查类型參数。

以下的代码给出了Manifest的使用方法:

object ManifestType extends App {
def print1[T](x: List[T])(implicit m: Manifest[T]) = {
if (m <:< manifest[String])
println("字符串类型的List")
else
println("非字符串类型的List")
}
print1(List("one", "two"))
print1(List(1, 2))
print1(List("one", 2))
}

隐式參数m由编译器依据上下文自己主动传入,比如print1(List(“one”, “two”)) ,编译器会依据”one”,”two” 实际类型判断出 T 的类型是 String,再隐式地传入了Manifest[String]类型的对象參数。使得执行时能够依据这个參数做很多其它的事情。

以下的代码演示了怎样使用TypeTag

import scala.reflect.runtime.universe._
def getTypeTag[T: TypeTag](a: T) = typeTag[T]
//下列语句返回TypeTag[List[Int]]
println(getTypeTag(List(1, 2, 3)))

从上面的代码能够看到,typeTag返回的是详细的类型,而不是类型擦除之后的类型any,即TypeTag保存全部详细的类型。在执行时能够通过模式匹配来精确地对类型进行判断:

import scala.reflect.runtime.universe._
def patternMatch[A : TypeTag](xs: List[A]) = typeOf[A] match {
//利用类型约束进行精确匹配
case t if t =:= typeOf[String] => "list of strings"
case t if t <:< typeOf[Int] => "list of ints"
}
println(patterMatch(List(1,2)))

上边的typeOf[A]在传入參数为List(“String”)时,得到结果是java.lang.String。

typeOf[A]接受一个类型为TypeTag[a]的隐式參数,编译器生成的TypeTag隐式參数会被传给typeOf[A] 。

有4种TypeTag:

1 scala.reflect.api.TypeTags#TypeTag. A full type descriptor of a Scala type. For example, a TypeTag[List[String]] contains all type information, in this case, of typescala.List[String].

2 scala.reflect.ClassTag. A partial type descriptor of a Scala type. For example, a ClassTag[List[String]] contains only the erased class type information, in this case, of type

3 scala.collection.immutable.List.ClassTags provide access only to the runtime class of a type. Analogous to scala.reflect.ClassManifest.

4 scala.reflect.api.TypeTags#WeakTypeTag. A type descriptor for abstract types (see corresponding subsection below).

这给出最经常使用的ClassTag的使用方法:ClassTag[T]保存了被泛型擦除后的原始类型T,提供给执行时程序使用。

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._ scala> val tt = typeTag[Int]
tt: reflect.runtime.universe.TypeTag[Int] = TypeTag[Int] scala> scala> import scala.reflect._
import scala.reflect._ //得到详细类型
scala> val ct = classTag[String]
ct: scala.reflect.ClassTag[String] = java.lang.String

3. Scala类型系统总结

到此,Scala的类型系统基本介绍完成。下表给出了Scala中常见的类型

类型 语法类型
class Person
特质 trait Closable
元组类型 (T1,T2,T3,…)
函数类型 (T1,T2,t3,…)=>T
參数类型(泛型) class Person[T1,T2,…]
单例类型 this.type
类型投影 Outter#Inner
复合类型 A with B with C…
结构体类型 {def f():Unit ….}
中置类型 T1 A T2
存在类型 T forSome {}

加入公众微信号,能够了解很多其它最新Spark、Scala相关技术资讯

Scala入门到精通——第二十四节 高级类型 (三)的更多相关文章

  1. Scala入门到精通——第二十九节 Scala数据库编程

    本节主要内容 Scala Mavenproject的创建 Scala JDBC方式訪问MySQL Slick简单介绍 Slick数据库编程实战 SQL与Slick相互转换 本课程在多数内容是在官方教程 ...

  2. Scala入门到精通——第十五节 Case Class与模式匹配(二)

    本节主要内容 模式匹配的类型 for控制结构中的模式匹配 option类型模式匹配 1. 模式的类型 1 常量模式 object ConstantPattern{ def main(args: Arr ...

  3. Scala入门到精通——第十九节 隐式转换与隐式參数(二)

    作者:摇摆少年梦 配套视频地址:http://www.xuetuwuyou.com/course/12 本节主要内容 隐式參数中的隐式转换 函数中隐式參数使用概要 隐式转换问题梳理 1. 隐式參数中的 ...

  4. Scala入门到精通——第十六节 泛型与注解

    本节主要内容 泛型(Generic Type)简单介绍 注解(Annotation)简单介绍 注解经常使用场景 1. 泛型(Generic Type)简单介绍 泛型用于指定方法或类能够接受随意类型參数 ...

  5. 风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击

    风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击 XSS钓鱼攻击 HTTP Basic Authentication认证 大家在登录网站的时候,大部分时候是通过一个表单提交登录信息. 但是有时候 ...

  6. Scala入门到精通——第二十七节 Scala操纵XML

    本节主要内容 XML 字面量 XML内容提取 XML对象序列化及反序列化 XML文件读取与保存 XML模式匹配 1. XML 字面量 XML是一种很重要的半结构化数据表示方式,眼下大量的应用依赖于XM ...

  7. 第二十四节,TensorFlow下slim库函数的使用以及使用VGG网络进行预训练、迁移学习(附代码)

    在介绍这一节之前,需要你对slim模型库有一些基本了解,具体可以参考第二十二节,TensorFlow中的图片分类模型库slim的使用.数据集处理,这一节我们会详细介绍slim模型库下面的一些函数的使用 ...

  8. Scala入门到精通——第二十二节 高级类型 (一)

    作者:摇摆少年梦 视频地址:http://www.xuetuwuyou.com/course/12 本节主要内容 this.type使用 类型投影 结构类型 复合类型 1. this.type使用 c ...

  9. [ExtJS5学习笔记]第二十四节 Extjs5中表格gridpanel或者表单数据后台传输remoteFilter设置

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/39667533 官方文档:http://docs.sencha.com/extjs/5. ...

随机推荐

  1. autoOpenBrowser: true, 运行npm后自动打开浏览器

    autoOpenBrowser: true, 运行npm后自动打开浏览器

  2. QT5:总结篇 控件集合

    一.Layouts 二.Spacers 三.Buttons 四.Item Views(Model-Based) 五.Item Widgets(Item-Based) 六.Containers 七.In ...

  3. 【分享】 封装js操作textarea 方法集合(兼容很好)。

    请使用下面的btn操作. 虽然你现在看来没什么用,当要用的时候又到处找资料,还不如现在收集一下.         在DOM里面操作textarea里面的字符,是比较麻烦的. 于是我有这个封装分享给大家 ...

  4. [IOS初学]ios 第一篇 storyboard 与viewcontroller的关系 - Zoe_J

    时间 2014-07-27 16:08:00  博客园-所有随笔区 原文  http://www.cnblogs.com/zoe-j/p/3871501.html 主题 StoryBoard 学习了一 ...

  5. API对接中经常会出现的签名获取,这只是某一种,仅供给有需要的人参考

    要求: 1.对所有传入参数(含系统参数和接口参数)按照字段名的 ASCII 码从小到大排序(字典序)后,使用 URL 键值对的格式.(即 key1=value1&key2=value2…)拼接 ...

  6. 4. COLLATION_CHARACTER_SET_APPLICABILITY

    4. COLLATION_CHARACTER_SET_APPLICABILITY 表COLLATION_CHARACTER_SET_APPLICABILITY表示哪种字符集适用于哪种排序规则. INF ...

  7. js 对象细节

    原型和原型链 在对象自身身上找不到指定属性时,就会到这个对象的原型__proto__上找,原型也是指向一个对象,在这个对象上还找不到对应属性,则继续到原型上来找...以上过程形成原型链. 访问对象的原 ...

  8. win7下qt error: undefined reference to `_imp__getnameinfo@28'解决

    _imp__getnameinfo@28对应着winsock2.h的getnameinfo函数 首先需要导入对应的头文件 #ifndef WIN32 #include <sys/socket.h ...

  9. Java:获取IP地址

    文章来源:https://www.cnblogs.com/hello-tl/p/9139323.html import java.net.InetAddress; import java.net.Un ...

  10. pycharm的快捷键以及常用设置

    1.编辑(Editing) Ctrl + Space 基本的代码完成(类.方法.属性) Ctrl + Alt + Space 快速导入任意类 Ctrl + Shift + Enter 语句完成 Ctr ...