隐式转换
在之前的类型学习中,我们已经学习了自动类型转换,精度小的类型可以自动转换为精度大的类型,这个转换过程无需开发人员参与,由编译器自动完成,这个转换操作我们称之为隐式转换。
在其他的场合,隐式转换也起到了非常重要的作用。如Scala在程序编译错误时,可以通过隐式转换中类型转换机制尝试进行二次编译,将本身错误无法编译通过的代码通过类型转换后编译通过。慢慢地,这也形成了一种扩展功能的转换机制。
1. 隐式函数
使用implicit关键字声明的函数称之为隐式函数。这样可以在不需改任何代码的情况下,扩展某个类的功能。
比如
scala
def main(args: Array[String]): Unit = {
// 转换函数
implicit def transformAge(age: Double): Int = {
age.toInt
}
val i:Int = 10.5
println(i)
}
运行结果:
2. 隐式参数&隐式变量
普通方法或者函数中的参数使用implicit
关键字声明为隐式参数,调用该方法时,编译器会在相应的作用域寻找符合条件的隐式变量, 隐式变量就是被implicit
关键字修饰的变量,没有找到就正常普通函数调用。
scala
def main(args: Array[String]): Unit = {
// 隐式参数
def reg(name:String)(implicit password:String="000000"):Unit={
println(s"name: $name, password: $password")
}
reg("jack")("111111")
reg("maomao")
println("***********************")
// 隐式参数需要配合隐式变量一起使用
implicit val pass = "123123"
reg("tuanzi")
}
运行结果:
3. 隐式类
3.1 隐式类概述
在Scala2.10之后提供了隐式类,可以使用implicit
声明类。
- 在没有隐式类之前,如果需要扩展类的功能,遵从OCP原则的话,可以这么实现:
scala
def main(args: Array[String]): Unit = {
// User类和UserExt的隐式转换,需要构造他们的关系
implicit def transformUser(user: User): UserExt = {
new UserExt()
}
val user = new User()
user.insertUser()
// 利用class+function,实现扩展类的功能
user.updateUser()
}
class UserExt() {
def updateUser(): Unit = {
println("updateUser")
}
}
class User {
def insertUser(): Unit = {
println("insertUser")
}
}
运行结果: 2. 使用隐式类实现, 代码更加简洁
scala
def main(args: Array[String]): Unit = {
val user = new User()
user.insertUser()
user.updateUser()
}
// 使用隐式类,编译器自动生成transformUser类似方法,实现绑定关系进行互转
// 值得注意的是隐式类的构造函数里的参数只能有一个。
implicit class UserExt(user: User) {
def updateUser(): Unit = {
println("updateUser")
}
}
class User {
def insertUser(): Unit = {
println("insertUser")
}
}
object User{
// 在伴生对象内,定义隐式类
// implicit class UserExt(user: User) {
// def updateUser(): Unit = {
// println("updateUser")
// }
// }
}
3.2 隐式类定义的位置
在隐式转换二次编译的时候,Scala编译器根据隐式类生成隐式转换的逻辑,前提需要知道要用到的隐式类的位置,才能够自动生成转换的逻辑,如果不明确限制查找范围,Scala编译器的效率可想而知,java中的包就要扫描很久。
- 隐式类的定义的位置:
- 当前类的内部, 隐式类不能定义为最外层对象
- 父类,父类的伴生对象
- 特质,特质的伴生对象
- 包对象
比较特殊的是包对象,将隐式类放到包对象过程如下:
创建包对象:
scala
object TransformDemo6 {
def main(args: Array[String]): Unit = {
val user = new User()
user.insertUser()
user.updateUser()
}
class User {
def insertUser(): Unit = {
println("insertUser")
}
}
}
scala
package object transformData {
implicit class UserExt(user: User) {
def updateUser(): Unit = {
println("updateUser")
}
}
}
运行结果:
总结
我们通过隐式函数,隐式参数,隐式类实现隐式转换的不同场景,底层也就是控制编译器的编译逻辑,这在Java中是不存在的操作,Scala比Java更加灵活。Scala的源码中应用很多隐式函数,比如可以参看Predef.scala源码: 又比如隐式参数的应用:
scala
def main(args: Array[String]): Unit = {
val list: List[Int] = List(3, 5, 1, 8, 2, 7)
// sortBy有两个参数列表,其中第二个表示隐式参数,
// 如果调用的时候,没有使用第二个参数列表,那么编译器会自动查找排序规则
// 今后遇到含有隐式参数的代码,编译器提示(...)错误,就表示隐式变量没有找到
println(list.sortBy(identity))
println(list.sortBy(identity)(Ordering.Int.reverse))
}