作者:摆摆少年梦

视频地址: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. gitlab利用ssh方式拉取代码

    问题1: Bad owner or permissions on .ssh/config的解决 当为本机配一个固定用户名远程登录某主机时,配置了一个config文件,但是在执行ssh免密码登录时报如下 ...

  2. less算宽度 加~ width: calc(~"50% - 35px");

    less算宽度 加~  width: calc(~"50% - 35px");

  3. Spread / Rest 操作符

    Spread / Rest 操作符指的是 ...,具体是 Spread 还是 Rest 需要看上下文语境. 当被用于迭代器中时,它是一个 Spread 操作符:(参数为数组) function foo ...

  4. No-10.高级变量类型

    高级变量类型 目标 列表 元组 字典 字符串 公共方法 变量高级 知识点回顾 Python 中数据类型可以分为 数字型 和 非数字型 数字型 整型 (int) 浮点型(float) 布尔型(bool) ...

  5. 微信小程序之裁剪图片成圆形

    前言 最近在开发小程序,产品经理提了一个需求,要求微信小程序换头像,用户剪裁图片必须是圆形,也在github上看了一些例子,一般剪裁图片用的都是方形,所以自己打算写一个小组件,可以把图片剪裁成圆形,主 ...

  6. [题解] codevs 1486 愚蠢的矿工

    http://codevs.cn/problem/1486/ 我们比较熟悉二叉树,题目中给出的是一棵多叉树,我们需要将这可二叉树改造成二叉树. 二叉树可以为这样的: 父亲结点左边储存儿子,右边储存兄弟 ...

  7. linux 作为web应用服务器内核参数/etc/sysctl.conf

    # Kernel sysctl configuration file for Red Hat Linux## For binary values, 0 is disabled, 1 is enable ...

  8. HTML5新增的非主体元素header元素、footer元素、hgroup元素、adress元素

    ---恢复内容开始--- header header元素是一种具有引导和导航作用的结构元素,通常用来放置整个页面或页面内的一个内容区块的标题,但是也可以包含其他内容,例如数据表格.搜索表单或相关的lo ...

  9. pycharm运行没问题,但是在命令行执行就报错

    因为python并不知道你那个叫demo的package在哪里.你需要手动把project的完整路径添加到PYTHONPATH这个环境变量中.pycharm执行项目中的文件时会自动帮你做这件事,所以你 ...

  10. Java高级程序员面试题

    1.你认为项目中最重要的过程是那些? 分析.设计阶段  尽量找出进度的优先级 2.如果给你一个4-6人的team,怎么分配? 挑选一技术过硬的人作为我的替补.其它人平均分配任务,每周进行全面的任务分配 ...