scala 隐式转换

隐式转换执行过程分析

概念

Java 有很庞大的类库资源,但是 被声明成 final 的类不允许继承 例如 String , 怎样扩展java及第三方类库 Scala提供了很灵活的方式

当 scala 使用 implicit 隐式转化时 , scala 编辑器发现对象的类型不匹配时,不会直接报错,而会在代码中尝试匹配implicit声明的object, 当然,相同方法签名的类必须唯一。
举个小例子,实现在字符串前后 添加 “**” 的操作。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.io.File
import scala.io.Source
/**
* 隐式转换
*/
class RichFile(val file:File){
def read() = Source.fromFile(file.getPath()).mkString
}
object Context{
implicit def file2RichFile(file:File) = new RichFile(file)
}
object hello_implicit_convertion{
def main(args: Array[String]) {
import Context.file2RichFile
println( new File("I:\\aa.txt").read())
}
}

代码的执行过程如下:

  1. 调用File 的read 方法
  2. 当编译器发现File类没有read 方法时,不是直接报错,而是执行第三步
  3. 检查当前作用域中是否有接受File的 implicit 方法,如没有直接报错,如有,执行第4步
  4. 将File作为参数实例化RichFile,再检查是否有read 方法,如没有直接报错
  5. 执行read方法

整个执行过程中,需要特别注意的是, 作用域

作用

  1. 功能增强

隐式参数实例解析

概念

所谓隐式参数,是指方法或者函数中implicit 关键字 修饰的参数
如:

1
2
3
def print(content:String)(implicit language:String): Unit ={
println(language +":" + content)
}

方法中 language参数,前面有implicit

实例

隐式参数的执行过程,在当前作用域中寻找 implicit 关键字修饰的 类型 相同的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 隐式参数
*/
object Context_Implicits{
implicit val defalut:String = "java" //2. implicit 限定的 类型为String 的 defalut 变量
}
object Param {
//implicit val defalut2:Int = 111
def print(content:String)(implicit language:String): Unit ={ // 1 implicit 限定的 类型为String 的 language 参数
println(language +":" + content)
}
}
object Implicit_Parameter {
def main(args: Array[String]) {
Param.print("spark")("scala")
import Context_Implicits._
import Param._
Param.print("spark") // 3.当执行缺省值的方法时,编译器不会立马报错,而是在当前作用域中寻找 implicit 限定的 类型相同的变量
}
}

执行过程中,会按照1,2,3的步骤进行。 如果作用域中有多个类型相同的同时都有impplict 限定,会出现什么情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 隐式参数
*/
object Context_Implicits{
implicit val defalut:String = "java" //2. implicit 限定的 类型为String 的 defalut 变量
}
object Param {
implicit val defalut2:String = "java2"
def print(content:String)(implicit language:String): Unit ={ // 1 implicit 限定的 类型为String 的 language 参数
println(language +":" + content)
}
}
object Implicit_Parameter {
def main(args: Array[String]) {
Param.print("spark")("scala")
import Context_Implicits._
import Param._
Param.print("spark") // 3.当执行缺省值的方法时,编译器不会立马报错,而是在当前作用域中寻找 implicit 限定的 类型相同的变量
}
}

执行过程立马报错:

1
2
3
Error:(27, 16) not enough arguments for method print: (implicit language: String)Unit.
Unspecified value parameter language.
Param.print(“Spark”) // 3.当执行缺省值的方法时,编译器不会立马报错,而是在当前作用域中寻找 implicit 限定的 类型相同的变量

隐式参数与隐式转换的联合使用解析

函数参数与隐式转换在 Scala 和 Spark 中经常出现,让隐式参数与隐式转换的联合使用,请看一下实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 隐式类型的隐式转换
*/
class Student(val name:String, val age: Int) extends Ordered[Student]{
override def compare(that: Student): Int = if(this.age >= that.age) 1 else -1
}
object Implicit_Conversion_With_Implicit_Paramter {
def main(args: Array[String]) {
def bigger[T](a:T,b:T)(implicit order: T => Ordered[T] ): T ={
if( a > b) a else b
}
println(bigger(2,3))
println(bigger(new Student("zhansan",20),new Student("lisi",40)).name)
}
}

根据上一节 隐式参数的定义,bigger 方法中 参数 order 被 implicit 限定,我们称之为 隐式参数,在这里只不过此参数为 一个 从 T 到ordered[T]的函数。 当调用 bigger(2,3),scala 根据类型推测出 Int 类型,同时检查在当前作用域里,有没有 implict 限定的 从 Int 到 Ordered[Int] 方法。 我们知道,scala 的 predef 中已经提供了这样的函数。这个函数函数其实就是隐式转换的函数

在看看 bigger(new Student(“zhansan”,20),new Student(“lisi”,40) 执行,由于 Student 类,不是系统自带函数,为了实现 Student => Ordered[Student] ,这里继承了 Ordered[Student] 类。

隐式类分析

概念

所谓隐式类: 就是对类增加 implicit 限定的类,其作用主要是对类的加强!

如:

1
2
3
implicit class ImpInt(tmp:Int){
def add(tmp2: Int) = tmp + tmp2
}

class 前面的 implicit ,通过这个隐式类,就可以让 Int 型数据具有 add 方法。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import scala.io.Source
import java.io.File
/**
* 隐式类
* 其作用主要是对类的加强
*
*/
object Implicit_Class {
import Context_Helper._
def main(args: Array[String]) {
println( 1.add(2)) //在当前作用域中寻找 将Int(1) 作为变量的类同时具有add 的方法的类,如有,则执行
println(new File("I:\\aa.txt").read()) //在当前作用域中寻找 将File 作为变量的类同时具有read的方法的类,如有,则执行
}
}
object Context_Helper{
implicit class ImpInt(tmp:Int){
def add(tmp2: Int) = tmp + tmp2
}
implicit class FileEnhance(file:File){
def read() = Source.fromFile(file.getPath).mkString
}
}

执行过程分析

  1. 当 1.add(2) 时,scala 编译器不会立马报错,而检查当前作用域有没有 用 implicit 修饰的,同时可以将 Int 作为参数的构造器,并且具有方法 add 的类,经过查找 发现 ImpInt 符合要求
  2. 利用隐式类 ImpInt 执行add 方法

隐式对象分析

what is 隐式对象

所谓隐式对象 : 就是用 implicit object 定义的对象,其作用主要表现在 运行时 被调用
如:

1
2
3
4
implicit object StringAdd extends SubTemplate[String]{
override def add(x: String, y: String): String = x+y
override def unit: String = ""
}

StringAdd 前面的 implicit object

when 使用隐式对象

对于一些根据类型推断的计算,形如:净价+{利息} = 全价,或者是一些 表达式语句的计算,如${净价 + 利息}等,更加复杂一点的是, 计算数据的类型不同的场景

how 使用隐式对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 隐式对象
*/
abstract class Template[T]{
def add(x:T,y:T):T
}
abstract class SubTemplate[T] extends Template[T]{
def unit:T
}
object Implicit_Object {
def main(args: Array[String]) {
implicit object StringAdd extends SubTemplate[String]{
override def add(x: String, y: String): String = x+y
override def unit: String = ""
}
implicit val wrongInt = new SubTemplate[Int]{
override def unit: Int = -1
override def add(x: Int, y: Int): Int = x+ y
}
implicit object IntAdd extends SubTemplate[Int]{
override def unit: Int = 0
override def add(x: Int, y: Int): Int = x+ y
}
def sum[T](xs:List[T])(implicit m: SubTemplate[T]) : T =
if(xs.isEmpty) m.unit else m.add(xs.head,sum(xs.tail))
println(sum(List(1,2,3,4,5)))
}
}

过程解析

执行过程检查当前作用是否有 implicit object 限定的 类型为 SubTemplate 的对象,如有,则选取此对象。

伴生对象的隐式转换

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 伴生对象 中的隐式方法
*/
import java.io.File
import scala.io.Source
class RichFile2(val file:File) {
def read = Source.fromFile(file.getPath).mkString
}
class File_Implicit2(path:String) extends File(path)
object File_Implicit2{
implicit def file2RichFile(file:File) = new RichFile2(file) // file => RichFile2
}
object Implicits_Internals {
def main(args: Array[String]) {
println(new File_Implicit2("I:\\aa.txt").read )
}
}

过程解析

当 new File_Implicit2 对象 执行read 过程中,并没有import 导入隐式转换相关方法,而是 通过 File_Implicit2 的伴生对象来完成。

当代码改为如下语句时:

1
2
3
4
5
6
7
8
9
10
object File_Implicit3{
implicit def file2RichFile(file:File) = new RichFile2(file) // file => RichFile2
}
object Implicits_Internals {
def main(args: Array[String]) {
import File_Implicit3._
println(new File_Implicit2("I:\\aa.txt").read )
}
}

代码执行的 不是伴生对象File_Implicit2 中的隐式转换,而是 File_Implicit3 中的。

热评文章