作者:摆摆少年梦

视频地址: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. instance of type of object.prototype.tostring 区别

    typeof typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型.   返回的结果用该类型的字符串(全小写字母)形式表示,包括以下 6 种:   number.boolea ...

  2. gocode安装不上的解决办法

    mkdir -p $GOPATH/src/golang.org/x  //路径下创建此文件 cd $GOPATH/src/golang.org/x      //切换到此目录 git clone ht ...

  3. win7下自动更新svn目录

    !!注意,文件编码必须是ANSI.否则中文路径会乱码 ::这里是svn安装目录 set svnExe="C:\Program Files\TortoiseSVN\bin\TortoisePr ...

  4. django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module

    pip3 install mysqlclient try again python manage.py makemigrations python manage.py migrate

  5. RPM Package Manager

    本文大部分内容来自鸟哥的Linux私房菜,并且由作者根据自己的学习情况做了一些更改,鸟哥原文链接 1. 程序的安装方式 源代码安装:利用厂商释出的Tarball 来进行软件的安装,每次安装程序都需要检 ...

  6. LeetCode(237)Delete Node in a Linked List

    题目 Write a function to delete a node (except the tail) in a singly linked list, given only access to ...

  7. LeetCode(19) Remove Nth Node From End of List

    题目 Given a linked list, remove the nth node from the end of list and return its head. For example, G ...

  8. 准备新的代码迁移到cnblogs

    http://www.cnblogs.com/danghuijian/ http://www.cnblogs.com/danghuijian/

  9. jQuery中attr和prop方法的区别

    jQuery中attr和prop方法的区别。 http://my.oschina.net/bosscheng/blog/125833 http://www.javascript100.com/?p=8 ...

  10. mysql异常Incorrect string value: '\xE6\xB5\x8B\xE8\xAF\x95' for column 'region_name'

    Incorrect string value: '\xE6\xB5\x8B\xE8\xAF\x95' for column 'region_name' insert语句加的该字段有汉字,乱码造成的 解 ...