【图文详细 】Scala——隐式转换和隐式参数

版权声明:版权声明:本文为博主原创文章,转载请附上博文链接! https://blog.csdn.net/qq_42246689/article/details/85231767

2、Scala 隐式转换和隐式参数 

隐式转换和隐式参数是 Scala 中两个非常强大的功能,利用隐式转换和隐式参数,你可以提 供优雅的类库,

对类库的使用者隐匿掉那些枯燥乏味的细节。 
 
隐式的对类的方法进行增强,丰富现有类库的功能 
 
是指那种以 implicit 关键字声明的带有单个参数的函数。

可以通过::implicit -v 这个命令显示所有做隐式转换的类。 

2.1、Scala 隐式转换探讨 

现在我们来考虑一个问题: 之前讲过:

1 to 10 其实可以写成 1.to(10)

那其实就是表示:1 是一个 Int 类型的变量,所以证明 Int 类中会有一个 to 的方法 但事实上,我们在 Int 类型中根本就没有寻找 to 方法 那也就是说对一个 Int 类型的变量 1 调用 Int 类型不存在的一个方法,这怎么还能正常运行 呢?

原因就是隐式转换 
 
看一个最简单的隐式转换的例子: 

那我们首先来看一下隐式参数: 

package com.mazh.scala.day3 
 
object ImplicitParamTest { 
  // 正常的普通方法 
  def add(x:Int, y:Int) = x + y 
 
  // 柯里化的方法 
  def add2(x:Int)(y:Int) = x + y 
  def add3(x:Int)(y:Int = 10) = x + y 
 
//  如果变成下面这种形式: 
  def add4(x:Int)(implicit y:Int = 10) = x + y 
 
  def main(args: Array[String]): Unit = { 
 
    println ( add (2,3)) 
    // 不能只传一个参数取使用,必须要传入两个参数 
    println ( add2 (2)(3)) 
    println ( add 3 (2)()) 
    // 调用带隐式参数的函数 
    println ( add 4 (2)) 
  } 
}

在上面的代码中,可以看出来,如果对 add2 方法的第二个参数,做了隐式声明,发现之前 需要传入两个参数才能执行的方法 add2 就可以只传入一个参数就能执行计算 
 
那有什么应用场景呢? 比如汇率计算!!!!! 

object ImplicitParamTest2 { 
  /** 
    *  第一个参数是要换算成美元的人民币数目 
    *  第二个参数是汇率 
    */ 
  def rmb(dollar:Double)(implicit rate:Double = 6) = dollar * rate 
 
  def main(args: Array[String]): Unit = { 
 
    println ( rmb (100)) 
    println ( rmb (100)(7)) 
 // 引入隐式转换值,所以第二个参数被隐式的转换成了 6.66 
    import MyPredef._ 
    println ( rmb (100)) 
  } 
} 
 
object MyPredef{ 
  // 声明一个 Double 类型的隐式转换值 
  implicit var current_rate :Double = 6.66 
}

总结:

1、 隐式转换会首先从全局中寻找,寻找不到,才使用隐式参数

2、 隐式转换只能定义在 object 中

3、 如果隐式转换存在二义性,那么程序会跑错 
 
那现在再来考虑:

对一个 Int 类型的变量 1 调用 Int 类型不存在的一个方法,程序能正常运行得到期待的结果,

没有抛错异常,到底是怎么回事?会不会就是 Int 类型的变量被隐式转换成了另一种包含 to 方法的类型了呢? 
 
1、首先,我们在 RichInt 中发现了 to 方法: 

2、查看系统是否为我们自动引入了默认的各种隐式转换: 在 Scala 交互命令行中执行命令:implicits -v 
 

scala> :implicit -v 
   /* 69 implicit members imported from scala.Predef */
   /* 7 inherited from scala */
   final implicit class ArrayCharSequence extends CharSequence 
   final implicit class ArrowAssoc[A] extends AnyVal
   final implicit class Ensuring[A] extends AnyVal
   final implicit class RichException extends AnyVal
   final implicit class SeqCharSequence extends CharSequence
   final implicit class StringFormat[A] extends AnyVal
   final implicit class any2stringadd[A] extends AnyVal 
 
   /* 40 inherited from scala.Predef */
   implicit def ArrowAssoc[A](self: A): ArrowAssoc[A]
   implicit def Ensuring[A](self: A): Ensuring[A]
   implicit def StringFormat[A](self: A): StringFormat[A]
   implicit def any2stringadd[A](self: A): any2stringadd[A] 
 
   implicit def booleanArrayOps(xs: Array[Boolean]): mutable.ArrayOps[Boolean]
   implicit def byteArrayOps(xs: Array[Byte]): mutable.ArrayOps[Byte]
   implicit def charArrayOps(xs: Array[Char]): mutable.ArrayOps[Char]
   implicit def doubleArrayOps(xs: Array[Double]): mutable.ArrayOps[Double]
   implicit def floatArrayOps(xs: Array[Float]): mutable.ArrayOps[Float]
   implicit def genericArrayOps[T](xs: Array[T]): mutable.ArrayOps[T]
   implicit def intArrayOps(xs: Array[Int]): mutable.ArrayOps[Int]
   implicit def longArrayOps(xs: Array[Long]): mutable.ArrayOps[Long]
   implicit def refArrayOps[T <: AnyRef](xs: Array[T]): mutable.ArrayOps[T]
   implicit def shortArrayOps(xs: Array[Short]): mutable.ArrayOps[Short]
   implicit def unitArrayOps(xs: Array[Unit]): mutable.ArrayOps[Unit] 
 
   implicit def $conforms[A]: <:<[A,A]
   implicit def ArrayCharSequence(__arrayOfChars: Array[Char]): ArrayCharSequence   
   implicit def Boolean2boolean(x: Boolean): Boolean
   implicit def Byte2byte(x: Byte): Byte
   implicit def Character2char(x: Character): Char
   implicit def Double2double(x: Double): Double
   implicit def Float2float(x: Float): Float
   implicit def Integer2int(x: Integer): Int
   implicit def Long2long(x: Long): Long
   implicit def RichException(self: Throwable): RichException
   implicit def SeqCharSequence(__sequenceOfChars: IndexedSeq[Char]): SeqCharSequence   
   implicit def Short2short(x: Short): Short
   implicit val StringCanBuildFrom: generic.CanBuildFrom[String,Char,String]
   implicit def augmentString(x: String): immutable.StringOps
   implicit def boolean2Boolean(x: Boolean): Boolean
   implicit def byte2Byte(x: Byte): Byte
   implicit def char2Character(x: Char): Character
   implicit def double2Double(x: Double): Double
   implicit def float2Float(x: Float): Float 
   implicit def int2Integer(x: Int): Integer
   implicit def long2Long(x: Long): Long
   implicit def short2Short(x: Short): Short
   implicit def tuple2ToZippedOps[T1, T2](x: (T1, T2)): runtime.Tuple2Zipped.Ops[T1,T2]   
   implicit def tuple3ToZippedOps[T1, T2, T3](x: (T1, T2, T3)):runtime.Tuple3Zipped.Ops[T1,T2,T3]
   implicit def unaugmentString(x: immutable.StringOps): String 
 
   /* 22 inherited from scala.LowPriorityImplicits */
   implicit def genericWrapArray[T](xs: Array[T]): mutable.WrappedArray[T]
   implicit def wrapBooleanArray(xs: Array[Boolean]): mutable.WrappedArray[Boolean]   
   implicit def wrapByteArray(xs: Array[Byte]): mutable.WrappedArray[Byte]
   implicit def wrapCharArray(xs: Array[Char]): mutable.WrappedArray[Char]
   implicit def wrapDoubleArray(xs: Array[Double]): mutable.WrappedArray[Double]   
   implicit def wrapFloatArray(xs: Array[Float]): mutable.WrappedArray[Float]
   implicit def wrapIntArray(xs: Array[Int]): mutable.WrappedArray[Int]
   implicit def wrapLongArray(xs: Array[Long]): mutable.WrappedArray[Long]
   implicit def wrapRefArray[T <: AnyRef](xs: Array[T]): mutable.WrappedArray[T]   
   implicit def wrapShortArray(xs: Array[Short]): mutable.WrappedArray[Short]
   implicit def wrapUnitArray(xs: Array[Unit]): mutable.WrappedArray[Unit] 
 
   implicit def booleanWrapper(x: Boolean): runtime.RichBoolean
   implicit def byteWrapper(x: Byte): runtime.RichByte
   implicit def charWrapper(c: Char): runtime.RichChar
   implicit def doubleWrapper(x: Double): runtime.RichDouble
   implicit def floatWrapper(x: Float): runtime.RichFloat
   implicit def intWrapper(x: Int): runtime.RichInt
   implicit def longWrapper(x: Long): runtime.RichLong
   implicit def shortWrapper(x: Short): runtime.RichShort
   implicit def unwrapString(ws: immutable.WrappedString): String
   implicit def wrapString(s: String): immutable.WrappedString 

通过观察发现,scala 会默认给我们引入 scala 中的 Predef.scala 中的所有隐式转换 

https://www.scala-lang.org/api/2.11.8/#scala.Predef$

最后在倒数第五行发现,有一个隐式方法能够把 Int 类型的变量转换成 runtime.RichInt 变量 符合我们的预期 

最终解释:

当调用了:1 to 10

其实是调用了:1.to(10)

但是:Int 中没有 to 方法 所以:去寻找引入的隐式转换中有没有能把 Int 类型转换成能执行 to 方法的类型

果然:在系统引入的转换中发现:implicit def intWrapper(x: Int): runtime.RichInt

所以:最终 int 类型的 1 就被转换成了 RichInt 类型的变量 

验证:RichInt 中确实存在 to 方法

最后:顺理成章的调用 RichInt(1).to(10)生成返回结果

结论:神奇但又合理 

2.2、隐式转换的发生时机  

到底在什么时候触发隐式转换呢? 

2.2.1、时机一:当调用某个对象不存在的方法时 

当一个对象去调用某个方法,但是这个对象并不具备这个方法。这个时候会触发隐式转换, 会把这个对象(偷偷的)隐式转换为具有这个方法的那个对象。这就和刚才解释的为什么 Int 类型没有 to 方法还是能够调用 to 方法,因为 Int 类型的变量 1 在调用 to 方法的时候,被隐 式转换成了 RichInt 的对象 

再次演示一个案例: 

object ImplicitTest2 { 
  def main(args: Array[String]): Unit = { 
    import FileImplicit._ 
    val file = new File("c:\\words.txt") 
    // file 对象是没有 readAll 方法的, 那么在调用一个不存在的方法的时候 
    // scala 会查看是否有隐式转换能把 file 对象转换成具有 readAll 方法对象 
    val allText = file.readAll() 
    println (allText) 
  } 
} 
 
class RichFile(f:File){ 
  def readAll():String = { 
    Source. fromFile (f).mkString 
  } 
} 
 
object FileImplicit{ 
 implicit def file2RichFile(f:File):RichFile = new RichFile(f) 
} 
 

2.2.2、时机二:当方法参数类型不匹配时 

正常情况下,我们在编写代码,如果去调用某个方法,确实这个方法也存在,要是传入的参 数类型不匹配,程序会抛错,这是再正常不过的事情了。 
 
可是 Scala 却在为我们做努力,努力帮助我们把这个方法调用执行起来。因为隐式转换的存 在 第一个案例:

object Demo009_Implicit_Type { 
 
  def main(args: Array[String]): Unit = { 
 
    // 定义一个隐式转换,能够、把一个 double类型的数编程 int类型。 
    implicit def double2Int(a:Double) = a.toInt 
 
    // 定义三个方法
    def sum1(x:Int, y:Int) = x + y 
    def sum2(x:Int, y:Double) = x + y 
    def sum3(x:Double, y:Double) = x + y 
 
    // 使用
     println(sum1(1, 2.0))     // 触发隐式转换
     println(sum2(1, 2))       // 触发隐式转换
     println(sum3(1,2))        // 触发隐式转换
   } 
} 

第二个案例: 

package com.mazh.scala.day3 
 
// 特殊人群 
class SpecialPerson(var name:String) 
// 特殊人群之一 
class Young(name:String) 
// 特殊人群之二 
class Older(name:String) 
// 正常人群之一 
class Worker(var name:String) 
// 正常人群之二 
class Adult(var name:String) 
 
class TicketHouse{ 
  def buyTicket(p:SpecialPerson): Unit ={ 
    println (p.name+"票给你!!爽去吧!!"); 
  } 
} 
object ObjectImplicit{ 
  implicit def object2special(obj:AneyRef):SpecialPerson={ 
// 这么 写的原因是给大家演示这三个 方法 的使用。其实 有 更简单的实现
 
    if(obj.getClass == classOf [Young]){ 
      val young = obj.asInstanceOf[Young] 
      new SpecialPerson(young.name) 
    }else if(obj.getClass == classOf [Older]){ 
      val older = obj.asInstanceOf[Older] 
      new SpecialPerson(older.name) 
    }else{ 
      new SpecialPerson("NULL") 
    } 
  } 
} 
object ImplicitTest3 { 
  def main(args: Array[String]): Unit = { 
    val ticketHouse = new TicketHouse() 
    val young = new Young("Young") 
    val older = new Older("Older") 
    val worker = new Worker("Worker") 
    val adult = new Worker("Adult") 
 
    import ObjectImplicit._ 
//  ticketHouse.buyTicke t(worker)    // 报错 
//  ticketHouse.buyTicket(adult)     // 报错 
    ticketHouse.buyTicket(young) 
    ticketHouse.buyTicket(older) 
  } 
} 

2.3、隐式转换忠告 

下面给出我自己开发实践中的部分总结,供大家参考:

 1、即使你能轻松驾驭 Scala 语言中的隐式转换,能不用隐式转换就尽量不用

 2、如果一定要用,在涉及多次隐式转换时,必须要说服自己这样做的合理

 3、如果只是炫耀自己的 Scala 编程能力,请大胆使用 

猜你喜欢

转载自blog.csdn.net/qq_42246689/article/details/85231767