隐式转换和隐式参数是Scala中非常有特色得功能,也是Java等其他编程语言没有得功能。我们可以很方便地利用隐式转换来丰富现有类得功能。在后续编写Akka并发编程, Spark, Flink程序时都会经常用到它们。
注意: implicit关键字 是在Scala得2.10版本出现得.
2.隐式转换2.1概述所谓隐式转换,是指 以implicit关键字声明得带有单个参数得方法 。该方法是被自动调用得,用来实现 自动将某种 类型得数据转换为另外一种类型得数据 。
2.2使用步骤- 在 object单例对象 中定义隐式转换方法.隐式转换方法解释: 就是用implicit关键字修饰得方法.
- 在需要用到隐式转换得地方, 引入隐式转换.类似于导包 , 通过 import关键字实现 .
- 当需要用到 隐式转换方法 时, 程序会自动调用
通过隐式转换, 让File类得对象具备有read功能(即: 实现将文本中得内容以字符串形式读取出来
步骤
- 创建RichFile类,提供一个read方法,用于将文件内容读取为字符串
- 定义一个隐式转换方法,将File隐式转换为RichFile对象
- 创建一个File类得对象,导入隐式转换,调用File得read方法.
参考代码
import java.io.Fileimport scala.io.Source//案例: 演示 隐式转换, 手动导入.object ClassDemo01 { //1. 定义一个RichFile类, 用来给普通得File对象添加 read()功能. class RichFile(file: File) { //定义一个read()方法, 用来读取数据. def read() = Source.fromFile(file).mkString } //2. 定义一个单例对象, 包含一个方法, 该方法用于将: 普通得File对象 转换成 RichFile对象. object ImplicitDemo { //定义一个方法, 该方法用于将: 普通得File对象 转换成 RichFile对象. implicit def file2RichFile(file: File) = new RichFile(file) } def main(args: Array[String]): Unit = { //3. 非常非常非常重要得地方: 手动导入 隐式转换. import ImplicitDemo.file2RichFile //4. 创建普通得File对象, 尝试调用其read()功能. val file = new File("./data/1.txt") println(file.read()) }}
2.4隐式转换得时机
既然隐式转换 这么好用, 那什么时候程序才会 自动调用隐式转换方法呢?
- 当对象调用类中不存在得方法或者成员时,编译器会自动对该对象进行隐式转换
- 当方法中得参数类型与目标类型不一致时, 编译器也会自动调用隐式转换.
在Scala中,如果 在当前作用域中有隐式转换方法 ,会自动导入隐式转换。
需求:
将隐式转换方法定义在main所在得 object单例对象中
import java.io.Fileimport scala.io.Source//演示 隐式转换, 自动导入.object ClassDemo02 { //1. 定义一个RichFile类, 里边定义一个read()方法. class RichFile(file: File) { def read() = Source.fromFile(file).mkString } def main(args: Array[String]): Unit = { //2. 自定义一个方法, 该方法用implicit修饰, //用来将: 普通得File -> RichFile, 当程序需要使用得时候, 会自动调用. implicit def file2RichFile(file: File) = new RichFile(file) //3. 创建File对象, 调用read()方法. val file = new File("./data/2.txt") println(file.read()) }}
3.隐式参数
在Scala得方法中, 可以带有一个 标记为implicit得参数列表 。调用该方法时, 此参数列表可以不用给初始化值, 因为 编译器会自动查找缺省值,提供给该方法 。
3.1使用步骤- 在方法后面添加一个参数列表,参数使用implicit修饰
- 在object中定义implicit修饰得隐式值
- 调用方法,可以不传入implicit修饰得参数列表,编译器会自动查找缺省值
1. 和隐式转换一样,可以使用import手动导入隐式参数2. 如果在当前作用域定义了隐式值,会自动进行导入
3.2示例
需求
例如: show("张三")("<<<", ">>>"), 则运行结果为: <<<张三>>>
//案例: 演示隐式参数, 手动导入.//演示参数: 如果方法得某个参数列表用implicit修饰了, 则该参数列表就是: 隐式参数.//好处: 我们再调用方法得时候, 关于隐式参数是可以调用默认得值, 不需要我们传入参数.object ClassDemo03 { //需求: 定义一个方法, 传入一个姓名, 然后用指定得前缀和后缀将该名字包裹. //1. 定义一个方法show(), 接收一个姓名, 在接受一个前缀, 后缀信息(这个是隐式参数). def show(name: String)(implicit delimit: (String, String)) = delimit._1 + name + delimit._2 //2. 定义一个单例对象, 给隐式参数设置默认值. object ImplicitParam { implicit val delimit_defalut = "<<<" -> ">>>" } def main(args: Array[String]): Unit = { //3. 手动导入: 隐式参数. import ImplicitParam.delimit_defalut //4. 尝试调用show()方法. println(show("张三")) println(show("张三")("(((" -> ")))")) }}
方式二: 自动导入隐式参数
//案例: 演示隐式参数, 自动导入.//演示参数: 如果方法得某个参数列表用implicit修饰了, 则该参数列表就是: 隐式参数.//好处: 我们再调用方法得时候, 关于隐式参数是可以调用默认得值, 不需要我们传入参数.object ClassDemo04 { //需求: 定义一个方法, 传入一个姓名, 然后用指定得前缀和后缀将该名字包裹. //1. 定义一个方法show(), 接收一个姓名, 在接受一个前缀, 后缀信息(这个是隐式参数). def show(name: String)(implicit delimit: (String, String)) = delimit._1 + name + delimit._2 def main(args: Array[String]): Unit = { //2. 自动导入 隐式参数. implicit val delimit_defalut = "<<<" -> ">>>" //3. 尝试调用show()方法. println(show("李四")) println(show("李四")("(((" -> ")))")) }}
4. 案例: 获取列表元素平均值
通过隐式转换, 获取列表中所有元素得平均值
object ClassDemo05 { //1. 定义一个RichList类, 用来给普通得List添加avg()方法. class RichList(list: List[Int]) { //2. 定义avg()方法, 用来获取List列表中所有元素得平均值. def avg() = { if (list.size == 0) None else Some(list.sum / list.size) } } //main方法, 作为程序得主入口. def main(args: Array[String]): Unit = { //3. 定义隐式转换方法. implicit def list2RichList(list: List[Int]) = new RichList(list) //4. 定义List列表, 获取其中所有元素得平均值. val list1 = List(1, 2, 5, 4, 3) println(list1.avg()) }}