背景
上一节,我介绍了scala中的面向对象相关概念,还有一个特色功能:模式匹配。本文,我会介绍另外一个特别强大的功能隐式转换,并在最后介绍scala中泛型的使用
1. 隐式转换
Scala提供的隐式转换和隐式参数功能,是非常有特色的功能,是Java等编程语言所没有的功能。它可以允许手动指定将某种类型的对象转换成其他类型的对象。
Scala的隐式转换,其实最核心的就是定义隐式转换函数,即implicit conversion function。定义的隐式转换函数,只要在编写的程序内引入,就会被Scala自动使用。Scala会根据隐式转换函数的签名,在程序中使用到隐式转换函数接收的参数类型定义的对象时,会自动将其传入隐式转换函数,转换为另外一种类型的对象并返回。这就是“隐式转换”
隐式转换函数叫什么名字是无所谓的,因为通常不会由用户手动调用,而是由Scala进行调用。但是如果要使用隐式转换,则需要对隐式转换函数进行导入。因此通常建议将隐式转换函数的名称命名为“one2one的形式。
Spark源码中有大量的隐式转换和隐式参数。学好隐式转换会对读懂spark源码有很大的帮助。
1.1. 隐式转换的概念
隐式转换是将类型A转换成类型B,但并不是A真的就成了B,而是A本来的属性仍存在的同时又拥有了B的属性,这使得A本身不发生变化的同时扩大了功能,此属于蒙面设计模式。又因为A直接使用了B的功能而不需要对A进行修改,因此此转换是隐式的,使用implicit修饰。简言之,隐式转换就是增强类型、扩展功能。
1.2. 隐式转换的适用情况
隐式转换主要适用于以下两种情况:
- 如果表达式e是类型S,并且S不符合表达式的期望类型T。
- 在具有类型S的e的e.m表达中,如果m不表示S的成员。
1.3. 隐式转换的原理
当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译。当想调用对象功能时,如果编译错误,那么编译器会尝试在当前作用域范围内查找能调用对应功能的转换规则,这个调用过程是由编译器完成的,所以称之为隐式转换,或称之为自动转换。
1.4 隐式转换的作用域
Scala编译器仅考虑作用域之内的隐式转换。要使用某种隐式操作,必须以单一标识符的形式(一种情况例外)将其带入作用域之内。例如:
object TestImplicit {
implicit def doubleToInt(x: Double) = x.toInt
}
object Test {
def main(args: Array[String]): Unit = {
// 以单一标识符引进doubleToInt的隐式转换
import TestImplicit._
val i: Int = 2.3
}
}
单一标识符有一个例外,编译器还将在源类型和目标类型的伴生对象中寻找隐式定义。
1.5. 隐式转换的规则
- 显示定义规则:在使用带有隐式参数的函数时,如果没有明确指定与参数类型匹配相同的隐式值,编译器不会通过额外的隐式转换来确定函数的要求。
- 作用域规则:不管是隐式值、隐式对象、隐式类还是隐式转换函数,都必须在当前的作用域使用才能起作用。
- 无歧义规则:不能存在多个隐式转换使代码合法。例如,代码中不应该存在两个隐式转换函数能够同时使某一类型转换为另一类型,也不应该存在相同的两个隐式值、主构造函数参数类型以及成员方法等同的两个隐式类。
- 一次性转换规则:隐式转换从源类型到目标类型只会经过一次转换,不会经过多次隐式转换达到。
1.6. 常见的隐式转换类型
1.6.1. 隐式转换函数
隐式转换函数的格式通常为:
implicit