Scala implicit


Scala implicit

implicit基本含義

在Scala中有一個關鍵字是implicit, 之前一直不知道這個貨是干什么的,今天整理了一下。

我們先來看一個例子:

def display(input:String):Unit = println(input)

我們可以看到,display函數的定義只是接受String類型的入參,因此調用display("any string")這樣的函數是沒問題的。但是如果調用display(1)這樣的入參不是String類型的話,編譯會出錯的。

如果我們想讓display函數也能夠支持Int類型的入參的話,除了我們重新定一個def display(input:Int):Unit = println(input)這樣的函數以外,我們還可以在相同的作用域內用implicit關鍵字定義一個隱式轉換函數,示例代碼如下:

object ImplicitDemo {

  def display(input:String):Unit = println(input)

  implicit def typeConvertor(input:Int):String = input.toString

  implicit def typeConvertor(input:Boolean):String = if(input) "true" else "false"

//  implicit def booleanTypeConvertor(input:Boolean):String = if(input) "true" else "false"


  def main(args: Array[String]): Unit = {
    display("1212")
    display(12)
    display(true)
  }

}

我們定義了2個隱式轉換函數:

  implicit def typeConvertor(input:Int):String = input.toString

  implicit def typeConvertor(input:Boolean):String = if(input) "true" else "false"

這樣display函數就可以接受String、Int、Boolean類型的入參了。注意到上面我們的例子中注釋的那一行,如果去掉注釋的那一行的話,會在運行的時候出現二義性:

Error:(18, 13) type mismatch;
 found   : Boolean(true)
 required: String
Note that implicit conversions are not applicable because they are ambiguous:
 both method typeConvertor in object ImplicitDemo of type (input: Boolean)String
 and method booleanTypeConvertor in object ImplicitDemo of type (input: Boolean)String
 are possible conversion functions from Boolean(true) to String
    display(true)
            ^

得出的結論是:

隱式轉換函數是指在同一個作用域下面,一個給定輸入類型並自動轉換為指定返回類型的函數,這個函數和函數名字無關,和入參名字無關,只和入參類型以及返回類型有關。注意是同一個作用域。

implicit的應用

我們可以隨便的打開scala函數的一些內置定義,比如我們最常用的map函數中->符號,看起來很像php等語言。
但實際上->確實是一個ArrowAssoc類的方法,它位於scala源碼中的Predef.scala中。下面是這個類的定義:

  final class ArrowAssoc[A](val __leftOfArrow: A) extends AnyVal {
    // `__leftOfArrow` must be a public val to allow inlining. The val
    // used to be called `x`, but now goes by `__leftOfArrow`, as that
    // reduces the chances of a user's writing `foo.__leftOfArrow` and
    // being confused why they get an ambiguous implicit conversion
    // error. (`foo.x` used to produce this error since both
    // any2Ensuring and any2ArrowAssoc pimped an `x` onto everything)
    @deprecated("Use `__leftOfArrow` instead", "2.10.0")
    def x = __leftOfArrow

    @inline def -> [B](y: B): Tuple2[A, B] = Tuple2(__leftOfArrow, y)
    def →[B](y: B): Tuple2[A, B] = ->(y)
  }
  @inline implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x)

我們看到def ->[B] (y :B)返回的其實是一個Tuple2[A,B]類型。
我們定義一個Map:

scala> val mp = Map(1->"game1",2->"game_2")
mp: scala.collection.immutable.Map[Int,String] = Map(1 -> game1, 2 -> game_2)

這里 1->"game1"其實是1.->("game_1")的簡寫。
這里怎么能讓整數類型1能有->方法呢。
這里其實any2ArrowAssoc隱式函數起作用了,這里接受的參數[A]是泛型的,所以int也不例外。
調用的是:將整型的1 implicit轉換為 ArrowAssoc(1)
看下構造方法,將1當作__leftOfArrow傳入。
->方法的真正實現是生產一個Tuple2類型的對象(__leftOfArrow,y ) 等價於(1, "game_id")
這就是一個典型的隱式轉換應用。

其它還有很多類似的隱式轉換,都在Predef.scala中:
例如:Int,Long,Double都是AnyVal的子類,這三個類型之間沒有繼承的關系,不能直接相互轉換。
在Java里,我們聲明Long的時候要在末尾加上一個L,來聲明它是long。
但在scala里,我們不需要考慮那么多,只需要:

scala> val l:Long = 10
l: Long = 10

這就是implicit函數做到的,這也是scala類型推斷的一部分,靈活,簡潔。
其實這里調用是:

val l : Long = int2long(10)

更牛逼的功能

為現有的類庫增加功能的一種方式,用java的話,只能用工具類或者繼承的方式來實現,而在scala則還可以采用隱式轉化的方式來實現。

隱式參數

看一個例子再說:

object ImplictDemo {

  object Context{
    implicit val ccc:String = "implicit"
  }


  object Param{
    def print(content:String)(implicit prefix:String){
      println(prefix+":"+content)
    }
  }

  def main(args: Array[String]) {
    Param.print("jack")("hello")

    import Context._
    Param.print("jack")
  }

}

程序運行結果為:

hello:jack
implicit:jack

隱式轉換擴展

import java.io.File

import scala.io.Source

class RichFile(val file:File){
  def read = Source.fromFile(file.getPath()).mkString
}

object Context{
  implicit def file2RichFile(f:File)= new RichFile(f)
}

object ImplictDemo {

  def main(args: Array[String]) {
    import Context.file2RichFile
    println(new File("f:\\create.sql").read)
  }

}

上面的代碼中調用的read方法其實就是RichFile中定義的read方法。

最后的總結:

  1. 記住隱式轉換函數的同一個scop中不能存在參數和返回值完全相同的2個implicit函數。
  2. 隱式轉換函數只在意 輸入類型,返回類型。
  3. 隱式轉換是scala的語法靈活和簡潔的重要組成部分

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM