Scala之隐式转换
概述
简单说,隐式转换就是:当Scala编译器进行类型匹配时,如果找不到合适的候选,那么隐式转化提供了另外一种途径来告诉编译器如何将当前的类型转换成预期类型。
隐式转换有四种常见的使用场景:
将某一类型转换成预期类型
类型增强与扩展
模拟新的语法
类型类
语法
隐式转换有新旧两种定义方法,旧的定义方法指是的“implict def”形式,这是Scala 2.10版本之前的写法,在Scala 2.10版本之后,Scala推出了“隐式类”用来替换旧的隐式转换语法,因为“隐式类”是一种更加安全的方式,对被转换的类型来说,它的作用域更加清晰可控。
接下来我们通过实例来了解这两种隐式转换的写法。前文提到,隐式转换最为基本的使用场景是:将某一类型转换成预期类型,所以我们下面的例子就以最这种最简单的场景来演示,它们都实现了:将一个String类型的变量隐式转换为Int类型:
“implict def”形式的隐式转换
package com.github.scala.myimplicit /**
* A demo about scala implicit type conversion.
* @author Laurence Geng
*/
object ImplicitDefDemo { object MyImplicitTypeConversion {
implicit def strToInt(str: String) = str.toInt
} def main(args: Array[String]) {
//compile error!
//val max = math.max("1", 2);
import MyImplicitTypeConversion.strToInt
val max = math.max("", );
println(max)
}
}
“隐式类”形式的隐式转换
package com.github.scala.myimplicit /**
* A demo about scala implicit type conversion.
* @author Laurence Geng
*/
object ImplicitClassDemo { implicit class MyImplicitTypeConversion(val str: String){
def strToInt = str.toInt
} def main(args: Array[String]) {
//compile error!
//val max = math.max("1", 2);
import com.github.scala.myimplicit.ImplicitDefDemo.MyImplicitTypeConversion._
val max = math.max("", );
println(max)
}
}
隐式类有如下几个限制:
They must be defined inside of another trait/class/object. They may only take one non-implicit argument in their constructor. There may not be any method, member or object in scope with the same name as the implicit class.
Note: This means an implicit class cannot be a case class.
隐式类与旧的隐式转换的语法(implicit def)是有细微的不同的,隐式类的运作方式是:隐式类的主构造函数只能有一个参数(有两个以上并不会报错,但是这个隐式类永远不会被编译器作为隐式类在隐式转化中使用),且这个参数的类型就是将要被转换的目标类型。从语义上这很自然:这个隐式转换类将包裹目标类型,隐式类的所有方法都会自动“附加”到目标类型上。
应用场景
转换成预期类型
对于这种使用场景实际上并不多见,实际意义也没有那么大。前文的代码就是这样这一场景的一个示例。
类型增强与扩展
真正让隐式转换大放异彩的是“类型增强”。这方面的示例是非常多的。
案例一:ArrayOps对Array的类型增强
一个典型案例是:Scala对Array对象进行的隐式转换。我们知道,Scala通过Predef声明了针对Array类型的两个隐式转换:一个是到ArrayOps的隐式转化,另一个是到WrappedArray的隐式转换。以前者为例,它为Array对象“添加”了大量的操作,这是通过隐式转换来”通明“的对一个类进行增强的典型案例!以下是Scala API文档中对这一技术细节的说明:
Two implicit conversions exist in scala.Predef that are frequently applied to arrays: a conversion to scala.collection.mutable.ArrayOps (shown on line of the example above) and a conversion to scala.collection.mutable.WrappedArray (a subtype of scala.collection.Seq). Both types make available many of the standard operations found in the Scala collections API. The conversion to ArrayOps is temporary, as all operations defined on ArrayOps return an Array, while the conversion to WrappedArray is permanent as all operations return a WrappedArray.
案例二:Spark中PairRDDFunctions对RDD的类型增强
如果你看一下Spark中的RDD以及它的子类是没有groupByKey, reduceByKey以及join这一类基于key-value元组的操作的,但是在你使用RDD时,这些操作是实实在在存在的,Spark正是通过隐式转换将一个RDD转换成了PairRDDFunctions, 这个动作是这样发生的:
首先在RDD的伴随对象中声明了从RDD到PairRDDFunctions的隐式转换:
然后在SparkContext中import了RDD的所有东西,使隐式转换生效。
模拟新的语法
这也是非常酷的一种应用场景。一个典型的应用场景就是Map中用于创建key-value元组的->符号,它就是一个隐式转换的产物。->不是 scala 本身的语法,而是类型 ArrowAssoc 的一个方法。这个类型定义在包 Scala.Predef 对象中。 Scala.Predef 自动引入到当前作用域,在这个对象中,同时定义了一个从类型 Any 到 ArrowAssoc 的隐含转换。因此当使用 1 -> “One”时,编译器自动插入从 1 转换到 ArrowAsso c转换。
类型类
类型类是一种非常灵活的设计模式,可以把类型的定义和行为进行分离,让扩展类行为变得非常方便。或者说如果我们想创建跨越类型的功能(即功能实现独立于类型去演变),那么这样的“功能”不是适合也不应该在主类型的层次结构上进行演变的,这时是使用类型类的绝佳场所。因为类型类是一个比较独立的语法,虽然它的实现需要使用到类型类,但是在本文中为了不止于失去焦点,我们不打算在这里详细介绍,而在接下来的一篇文章中进行专门的介绍。
隐式解析的搜索范围
这一部分的规则有些复杂,根据《Scala In Depth》所描述的,顶层的搜索逻辑是:
在当前作用域下查找。这种情形又分两种情况,一个是在当前作用域显示声明的implicit元素,另一个通过import导入的implicit元素。
如果第一种方式没有找到,则编译器会继续在隐式参数类型的隐式作用域里查找。
真正复杂的地方是什么叫一个类型的隐式作用域?一个类型的隐式作用域指的是“与该类型相关联的类型”的所有的伴生对象。
OK,那什么叫作“与一个类型相关联的类型”? 定义如下:
假如T是这样定义的:T with A with B with C,那么A, B, C的伴生对象都是T的搜索区域。
如果T是类型参数,那么参数类型和基础类型都是T的搜索部分。比如对于类型List[Foo],List和Foo都是搜索区域
如果T是一个单例类型p.T,那么p和T都是搜索区域
如果T是类型注入p#T,那么p和T都是搜索区域。
隐式参数
为什么把隐式参数单独拿出来放到最后讲是因为从用意上讲,隐式参数与我们前面讲述的隐式类型转化有很大的差异,虽然它涉及到了关键字implict,但是它做的是另外一件事情。隐含参数有点类似缺省参数,如果在调用方法时没有提供某个参数,编译器会在当前作用域查找是否有符合条件的 implicit 对象可以作为参数传入,不同于缺省参数,隐式参数的值可以在方法调用的前的上下文中指定,这是隐式参数更加灵活的地方。
object ImplicitParamDemo { object Greeter{
def greet(name:String)(implicit prompt: String) {
println("Welcome, " + name + ". The System is ready.")
println(prompt)
}
} def main(args: Array[String]) { implicit val prompt = ">" Greeter.greet("admin")
} }
Scala之隐式转换的更多相关文章
- scala自定义隐式转换
Scala自定义隐式转换 一.编写隐式转换类 /** * Author Mr. Guo * Create 2019/4/20 - 17:40 */ object StringImprovments { ...
- Scala学习——隐式转换
scala隐式转换 一.需求:为一个类添加一个新的方法 java:动态代理 scala:隐式转换 隐式转换例子: 1.man to superMan package top.ruandb.scala. ...
- 深入理解Scala的隐式转换系统
摘要: 通过隐式转换,程序员可以在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性可以极大的减少代码量,忽略那些冗长,过于细节的代码. 使用方式: 1. ...
- Scala模式匹配| 隐式转换
1. 模式匹配 Scala中的模式匹配类似于Java中的switch语法,但是更加强大.模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分 ...
- 转载:深入理解Scala的隐式转换系统
摘要: 通过隐式转换,程序员可以在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性可以极大的减少代码量,忽略那些冗长,过于细节的代码. 使用方式: 1. ...
- Scala之隐式转换implicit详解
假设我们有一个表示文本的行数的类LineNumber: class LineNumber ( val num : Int ) 我们可以用这个类来表示一本书中每一页的行数: val lineNumOfP ...
- Scala学习之路 (八)Scala的隐式转换和隐式参数
一.概念 Scala 2.10引入了一种叫做隐式类的新特性.隐式类指的是用implicit关键字修饰的类.在对应的作用域内,带有这个关键字的类的主构造函数可用于隐式转换. 隐式转换和隐式参数是Scal ...
- 深入理解Scala的隐式转换
摘要: 通过隐式转换,程序员可以在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性可以极大的减少代码量,忽略那些冗长,过于细节的代码. 使用方式: 1. ...
- scala的隐式转换
摘要: 通过隐式转换,程序员可以在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性可以极大的减少代码量,忽略那些冗长,过于细节的代码. 使用方式: 1. ...
随机推荐
- 时间模块和random模块
时间模块 和时间有关系的我们就要用到时间模块.在使用模块之前,应该首先导入这个模块. #常用方法 1.time.sleep(secs) (线程)推迟指定的时间运行.单位为秒. 2.time.time( ...
- 使用 systemctl 创建 ss 开机
有自启动脚本.可以设置开机自启. 下载python 安装 ss就不说了.使用 systemctl 创建ss开机自启服务. 创建配置文件 vim /usr/lib/systemd/system/shad ...
- Django中,ajax检测注册用户信息是否可用?
ajax检测注册用户信息主体思路 1.在settings.py中配置需要使用的信息 #对static文件进行配置 STATICFILES_DIRS=[ os.path.join(BASE_DIR,'s ...
- 初学的linux命令行
这几条命令是今天刚初学的,以前总看别人输入命令,好利落,到自己了,真心觉得难.目前就学了这几个命令.后期等学会了,再进行添加 vm 文件名 ——> 新建文件: :wq ——> 保存并退 ...
- [No0000E3]C# 数据类型
在 C# 中,变量分为以下几种类型: 值类型(Value types) 引用类型(Reference types) 指针类型(Pointer types) 值类型(Value types) 值类型变量 ...
- webkit下面的CSS设置滚动条
webkit下面的CSS设置滚动条 1.主要有下面7个属性: ::-webkit-scrollbar 滚动条整体部分,可以设置宽度啥的 ::-webkit-scrollbar-button 滚动条两端 ...
- PostgreSQL源码安装文档
This document describes the installation of PostgreSQL using the source code distribution. (If yo ...
- C和C指针小记(十一)-递归和迭代优化
1.递归 C通过运行时堆栈支持递归函数的实现. 递归函数就是直接或间接调用自身的函数. 一个小例子: /** 使用递归将整型转换为ascii字符 @param value 整型数 */ void bi ...
- php性能提升与检测
1.使用xhprof分析器检测性能各种消耗 2.php-fpm中进程池的配置参数查看最大进程数.进程最大处理http请求量.进程时间过多的http请求.每个进程使用的最大内存. 参考地址:https: ...
- php源码笔记
php global 你global了一个变量,那么Zend就会去全局symbol_table去寻找,如果找不到,就会在全局symbol_table中分配相应的变量.通过这样的机制,实现了全局变量. ...