Scala入门到精通——第二十四节 高级类型 (三)
作者:摆摆少年梦
视频地址:http://blog.csdn.net/wsscy2004/article/details/38440247
本节主要内容
- Type Specialization
- Manifest、TypeTag、ClassTag
- 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入门到精通——第二十四节 高级类型 (三)的更多相关文章
- Scala入门到精通——第二十九节 Scala数据库编程
本节主要内容 Scala Mavenproject的创建 Scala JDBC方式訪问MySQL Slick简单介绍 Slick数据库编程实战 SQL与Slick相互转换 本课程在多数内容是在官方教程 ...
- Scala入门到精通——第十五节 Case Class与模式匹配(二)
本节主要内容 模式匹配的类型 for控制结构中的模式匹配 option类型模式匹配 1. 模式的类型 1 常量模式 object ConstantPattern{ def main(args: Arr ...
- Scala入门到精通——第十九节 隐式转换与隐式參数(二)
作者:摇摆少年梦 配套视频地址:http://www.xuetuwuyou.com/course/12 本节主要内容 隐式參数中的隐式转换 函数中隐式參数使用概要 隐式转换问题梳理 1. 隐式參数中的 ...
- Scala入门到精通——第十六节 泛型与注解
本节主要内容 泛型(Generic Type)简单介绍 注解(Annotation)简单介绍 注解经常使用场景 1. 泛型(Generic Type)简单介绍 泛型用于指定方法或类能够接受随意类型參数 ...
- 风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击
风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击 XSS钓鱼攻击 HTTP Basic Authentication认证 大家在登录网站的时候,大部分时候是通过一个表单提交登录信息. 但是有时候 ...
- Scala入门到精通——第二十七节 Scala操纵XML
本节主要内容 XML 字面量 XML内容提取 XML对象序列化及反序列化 XML文件读取与保存 XML模式匹配 1. XML 字面量 XML是一种很重要的半结构化数据表示方式,眼下大量的应用依赖于XM ...
- 第二十四节,TensorFlow下slim库函数的使用以及使用VGG网络进行预训练、迁移学习(附代码)
在介绍这一节之前,需要你对slim模型库有一些基本了解,具体可以参考第二十二节,TensorFlow中的图片分类模型库slim的使用.数据集处理,这一节我们会详细介绍slim模型库下面的一些函数的使用 ...
- Scala入门到精通——第二十二节 高级类型 (一)
作者:摇摆少年梦 视频地址:http://www.xuetuwuyou.com/course/12 本节主要内容 this.type使用 类型投影 结构类型 复合类型 1. this.type使用 c ...
- [ExtJS5学习笔记]第二十四节 Extjs5中表格gridpanel或者表单数据后台传输remoteFilter设置
本文地址:http://blog.csdn.net/sushengmiyan/article/details/39667533 官方文档:http://docs.sencha.com/extjs/5. ...
随机推荐
- WPF知识点全攻略00- 目录
知识点目录如下: 1.WPF相对WinFrom的优缺点 2.WPF体系结构 3.XAML 4.XAML页面布局 5.XAML内容控件 6.WPF中的“树” 7.Binding 8.依赖属性 9.附加属 ...
- QT+模态对话框与非模态对话框
#include "mainwindow.h" #include <QMenuBar> #include <QMenu> #include <QAct ...
- Redis那些事(一) — Redis简介
本人最近在学习Redis的使用和底层原理,有一些收获,所以希望通过写博客的形式来记录自己的学习过程,加深自己的理解,同时也方便以后查阅复习.目前打算先记录一些基本的使用方法和部分底层实现,其他的如果有 ...
- python基础(二) —— 流程控制语句
编程语言中的流程控制语句分为以下几类: 顺序语句 分支语句 循环语句 其中顺序语句不需要单独的关键字来控制,就是按照先后顺序一行一行的执行,不需要特殊的说明. 下面主要是 分支语句 和 循环语句的说明 ...
- RHEL6.5 DHCP服务器搭建
RHEL6.5 DHCP服务器搭建: DHCP服务器是用来分配给其它客户端IP地址用的,在RHEL 6.5中DHCP服务器搭建方法如下: 第一步,通过yum安装dhcp服务: 命令:yum insta ...
- CSS3---变形与动画效果
1.旋转rotate()函数通过指定的角度参数使元素相对原点进行旋转.它主要在二维空间内进行操作,设置一个角度值,用来指定旋转的幅度.如果这个值为正值,则元素相对原点中心顺时针旋转:如果这个值为负值, ...
- python之 集合 学习笔记
""" 集合内的元素是无序的,集合内的元素必须是可哈希的集合内元素的唯一的,不存在重复列表和字典不能存在集合里面,因为列表字典可变 可哈希集合也是不可哈希的 unhash ...
- Leetcode 289.生命游戏
生命游戏 根据百度百科,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机. 给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞.每个细胞具有一个初始状 ...
- 动手实操(一):如何用七牛云 API 实现相片地图?
实操玩家: 在苹果手机上,我们只要打开定位服务,拍照后便能在相簿中找到地图,地图上显示着在各地拍摄的相片.网站上这种显示方式也并不少见,例如 Flickr.即将关闭的 Panoramio 等. 作为地 ...
- Java并发编程:自己动手写一把可重入锁
关于线程安全的例子,我前面的文章Java并发编程:线程安全和ThreadLocal里面提到了,简而言之就是多个线程在同时访问或修改公共资源的时候,由于不同线程抢占公共资源而导致的结果不确定性,就是在并 ...