scala的隐式转换基本使用

15 阅读4分钟

(一)什么是隐式转换

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

package imp

/*
 * 隐式函数
 *
 */

object imp01 {

  implicit def double2Int(d:Double):Int = {
    println("调用 double2Int")
    d.toInt
  }
  def main(args: Array[String]): Unit = {
    var i:Int = 1;

    val d:Double = i // 把int --> double。 隐式转换。把精度低的数据转换成精度高的数据,这是可以的!

    i = 1.5 // 把double --> int。 隐式转换。把精度高的数据转换成精度低的数据,这是不可以的!

    println(i)
  }
}

应用场景

上游数据类型的变化,导致下游消费数据的变化。此时,可以在不修改原来代码的情况下,去很方便地解决这个问题。

改进:

package imp

/*
 * 隐式转换函数:把double转成int
 *
 * 隐式函数
 * 1.implicit 修饰
 * 2.它会自动被调用。当系统检测到隐式转换失败的时候,去自动调用。
 * 3.函数的名字不重要,重要的是它的输入数据的类型和返回值类型!
 *
 * 它的作用是:偷偷地把一种类型的数据转成另一种类型的数据!
 */

object imp02 {

  implicit def double2Int(d:Double):Int = {
    println("调用 double2int")
    d.toInt
  }
  def main(args: Array[String]): Unit = {
    var i:Int = 1;

    val d:Double = i // 把int --> double。 隐式转换。把精度低的数据转换成精度高的数据,这是可以的!

    i = 1.2 // 把double --> int。 隐式转换。把精度高的数据转换成精度低的数据,这是不可以的!
    i = 2.2
    i = 3.3

    println(i)
  }
}

如果代码的过程中,出现了类型不一致的问题,而我们要遵守OCP开发原则,所以不去修改业务代码。此时,选择让编译器去自动查找转换操作(第一次转换失败了,第二次去找规则,所以也叫二次编译)

(二)隐式参数

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

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

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

package imp

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

object imp03 {

  implicit val defaultPassword = "88888888"
  def reg(name:String)(implicit password:String="123456"):Unit = {
    println(s"用户名:$name,密码:$password")
  }
  def main(args: Array[String]): Unit = {
    reg("小花")
    reg("小明")("admin")

  }
}

(四)转换规则

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

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

 

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

有两个转换函数,代码在编译时,无法确定使用哪一个。

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

package imp

/*
 *隐式转换函数的规则
 * 1.无歧义规则
 *   不能写两个相同的转换函数
 *
 * 2.不能自动多次转换
 * A -> B, B -> C, 不等价于 A -> C
 */

object imp05 {
  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 * 10) }
  implicit def bm2m(bm:BM):M = { new M(bm.value * 100) }
//  implicit def m2km(m:M):KM = { new KM(m.value / 1000) }

  // 补充隐式转换函数,把BM -> M
  def main(args: Array[String]): Unit = {
    val km1 = new KM(2)
    val bm1:BM = km1
    
    //val m1:M = bm1
    val m1:M = km1
    println(bm1)
    println(m1)
    
//    val m1:M = km1
//    val m2:M = new BM(250)
//    
//    val km2:KM = new M(2500)
//
//    println(m1)
//    println(m2)
//    println(km2)
  }
}

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