(一)什么是隐式转换
隐式转换是指:scala自动把一种类型转成另一种类型的过程。这个过程对用户编程而言不可见,不需要用户手动编写代码。
请大家一起回顾如下代码
val b:Byte = 10;
val i:Int = b
在上面的代码的过程中,scala就帮我们做了一次隐式转换:把Int -> Double。
(二)隐式函数
在scala.Predef中,可以看到有系统提供了默认的情况。
隐式函数的定义:通过implicit关键字修饰的函数,它的功能是把一种类型的数据转成另一种类型。
implicit def 函数名(参数:类型1):类型2 = {
函数体
}
注意点:
(1)调用时机,在系统自动把数据从类型1隐式转为类型2时出错时,被自动调用。
(2)函数名可以自定义(一般取名为类型1 to 类型2),但是参数和返回值必须有限制。
object test {
// 与函数名没有关系
implicit def xxxxxx(x: Int): String = {
println("intToString")
x.toString
}
object imp01 {
// 隐式转换函数
// 1. 用关键字implicit 修饰
// 2. 它的函数名不重要(),重要的是它的 参数和返回值 的类型
// 3. 系统会自动调用它
// 注意:不能写两个同类型(参数和返回值的类型一样)的隐式转换函数,他会报错
// 它的参数是Double,它的返回值是Int,它的作用是: 当代码中出现了需要把Double转换成Int的时候,系统会自动调用它
implicit def double1Int(x:Double):Int = {
println("double to int ...")
x.toInt
}
// implicit def dddd(x:Double):Int = {
// println("double to int ...")
// x.toInt
// }
def main(args: Array[String]): Unit = {
// 1.隐式转换
var i:Int = 1
var d:Double = 1.1
d = i // 把int → double类型,他会自动也去调用隐式转换函数!
// d = i.toDouble
// println(s"d = ${d}")
i = d // 存在一个隐式转换 把double → int 报错!
}
}
结果:
double to int ...
(三)隐式参数
在开发的过程中,如果知道有个参数会被改动,就可以添加implicit,但是implicit在参数列表中只能出现一次,并要写在参数列表的最前面,对所有的参数列表生效。
// 在写代码的过程中,有一个原则:尽量不要修改之前的代码
// 隐式参数
// 在开发的过程中,如果预判一个参数将来会变动,就可以把它设置为implicit
object imp02 {
implicit val abc:String = "88888888" // 在修改隐式参数的值的时候,前面定义的变量名 不重要,类型是重要的
def reg( name:String)(implicit password:String= "123456"):Unit = {
println(s"您注册成功,姓名${name},密码${password}")
}
// def reg( name:String,password:String= "123456"):Unit = {
// println(s"您注册成功,姓名${name},密码${password}")
// }
def main(args: Array[String]): Unit = {
reg("小花") // 没有传入密码,使用默认值123456
reg("小白")("admin")
}
}
结果:
您注册成功,姓名小花,密码88888888
您注册成功,姓名小白,密码admin
如果一个函数有多个参数,并且你只想对某个参数进行隐式转换,那么,可以使用柯里化的方式来定义参数,并且把转换的参数定义在参数列表的最后。
(四)转换规则
隐式转换确实非常的强大,但是,在具体的使用过程中,我们要遵循一些特定的转换规则。
具体来说有两点:无歧义规则,不能多次转换。
规则1:无歧义规则
不能重新定义两个相同的转换函数。如果有两个隐式转换函数,只有函数名不同,则会导致错误。
规则2:不能多次转换
在一次转换过程中可以把A->B,B->C,但是不能直接从A->C。来通过具体的代码来说明。
// 隐私转换规则
// 1. 无歧义规则。不能重新定义两个相同的转换函数!
// 2. 不能多次转换
// 在一次转换中,A → B,B → C。不能直接 A → C
// implicit def fun1(a:Int):String = a.toString
// implicit def fun2(b:Int):String = b.toString
object imp03 {
class KM(var value:Double) {override def toString: String = s"${value}千米"}
class BM(var value:Double) {override def toString: String = s"${value}百米"}
class M(var value:Double) {override def toString: String = s"${value}米"}
implicit def km2bm(km:KM):BM = new BM(km.value*10)
implicit def bm2m(bm:BM):M = new M(bm.value*100)
implicit def km2m(km:KM):M = new M(km.value*1000)
def main(args: Array[String]): Unit = {
val km = new KM(1.1)
val bm:BM = km
println(km)
println(bm)
val m:M = bm
println(m)
val m1:M = new KM(2)
println(m1)
}
}
结果:
1.1千米
11.0百米
1100.0米
2000.0米
(五)作用域
就是把隐式函数的定义放在哪里使用起来更加方便?当然了,在默认情况下,会在当前类的内部去找。
先说一个不能放的位置:implicit 不能放在顶级作用域(不能放在类的外边)看如下代码,就会报错
在实际的操作过程中,我们建议把它定义在如下两个地方:
1.包对象。这样在这个包下的所有类和对象中都可以使用,并且不需要额外的引入语句。
- 一个单独的文件定义中。这样在其他需要的位置就可以直接导入,然后使用。
package implications
object Implictions {
implicit def double1Int(x:Double):Int = {
println("double to int ...")
x.toInt
}
}
在其他需要使用的地方导入:
import implication.Implications._