Scala—隐式转换的基本使用

15 阅读4分钟

一、什么是隐式转换

隐式转换是指:scala自动把一种类型转成另一种类型的过程。这个过程对用户编程而言不可见,不需要用户手动编写代码。

var i:Int= 1;

val b:Double = i

在上面的代码的过程中,scala就帮我们做了一次隐式转换:把Int -> Double。

但是反过来,不能把Double → Int

var i:Int= 1.5;  // 这里会报错

二、隐式函数

在scala.Predef中,可以看到有系统提供了默认的情况。

隐式函数的定义:通过implicit关键字修饰的函数,它的功能是把一种类型的数据转成另一种类型。

implicit def 函数名(参数:类型1):类型2 = {

函数体

}

注意点:

(1)调用时机,在系统自动把数据从类型1隐式转为类型2时出错时,被自动调用。

(2)函数名可以自定义(一般取名为类型1to类型2),但是参数和返回值必须有限制。

  • 隐式转换函数:把double转成int
  • 隐式函数
  • 1.implicit 修饰。
  • 2.它会自动被调用。当系统检测到隐式转换失败的时候,去自动调用。
  • 3.函数的名字不重要,重要的是它输入的数据类型和返回值类型!
  • 它的作用是:偷偷地把一种类型的数据转换成另一种类型的数据1
implicit def double2int(d:Double):Int = {
  println("调用 double2int")
  d.toInt
}

def main(args: Array[String]): Unit = {
  var i:Int = 1;

  val d:Double = i

  i = 1.2
  i = 2.2
  i = 3.3

  println(i)
}

三、隐式参数

如果一个参数的默认值会反复修改

def reg(name:String, password:String="000000"):Unit = {  
  println()// 注册用户名,默认密码为00000  
}  
reg("张三", "111111")  
reg("李四")

新的领导说用户密码全是0,在键盘的右边,对使用者不友好。要改成123123

你看,这样就修改了原来的代码。

在开发的过程中,如果知道有个参数会被改动,就可以添加implicit,但是implicit在参数列表中只能出现一次,并要写在参数列表的最前面,对所有的参数列表生效。

如果一个函数有多个参数,并且你只想对某个参数进行隐式转换,那么,可以使用柯里化的方式来定义参数,并且把转换的参数定义在参数列表的最后。

def reg(name:String)(implicit password:String = "000000"):Unit = {  
  println(s"name:${name}, password: ${password}")  
}  
  
reg("小花") // name:小花, password: 000000  
reg("小花")("1111111") //name:小花, password: 1111111

改进之后的代码:

    implicit val defaultPassword:String = "123123"  
    def reg(name:String)(implicit password:String = "000000"):Unit = {  
      println(s"name:${name}, password: ${password}")  
    }  
    reg("小花") // name:小花, password: 123123  
    reg("小花")("1111111") //name:小花, password: 1111111

隐式参数:如果在写函数的参数的时候,预计某个参数的默认值可能会被修改。可以设置隐式参数。

四、转换规则

隐式转换确实非常的强大,但是,在具体的使用过程中,我们要遵循一些特定的转换规则。

具体来说有两点:无歧义规则,不能多次转换。下面我们分别来解释:

规则1:无歧义规则

不能重新定义两个相同的转换函数。如果有两个隐式转换函数,只有函数名不同,则会导致错误。

来结合具体的代码说明

如下的代码会导致编译错误。

implicit def fun1(a:Int):String = a.toString  
  implicit def fun2(b:Int):String = b.toString

错误的原因是这里有两个转换函数,导致代码在编译时,无法确定使用哪一个。

规则2:不能多次转换

在一次转换过程中可以把A->B,B->C,但是不能直接从A->C。来通过具体的代码来说明。

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} 米"
}
// 补充隐式转换函数,把KM -> M
implicit def km2m(km:KM):M = {new M(km.value * 1000)}
// 补充隐式函数,把BM -> M
implicit def bm2m(bm:BM):M = {new M(bm.value * 100)}

在上面的代码中,可以把M->BM, 把BM->KM,但是依靠这两个规则自动推导M->KM就会报错。也就是说:隐式转换时只能转换一次。