好程式設計師大資料教程Scala系列之隱式轉換和隱式引數

好程式設計師IT發表於2019-10-16

好程式設計師大資料教程Scala系列之隱式轉換和隱式引數

5.1.  概念

隱式轉換和隱式引數是Scala中兩個非常強大的功能,利用隱式轉換和隱式引數,你可以提供優雅的類庫,對類庫的使用者隱匿掉那些枯燥乏味的細節。

5.2.  作用

隱式的對類的方法進行增強,豐富現有類庫的功能

object ImplicitDemo  extends App{
   // 定義隱式類,可以把 File 轉換成定義的隱式類 RichFile
   implicit  class RichFile(from:File){
     def read:String = Source.fromFile(from.getPath).mkString
  }
   // 使用隱式類做已有類的動能的擴充套件
   val contents =  new File("src/test1.txt").read
  println(contents)

}

5.5.  隱式類

建立隱式類時,只需要在對應的類前加上implicit關鍵字。比如:

object Helpers {
   implicit  class IntWithTimes(x: Int) {
     def times[A](f: => A): Unit = {
       def loop(current: Int): Unit =
         if(current > 0) {
          f
          loop(current - 1)
        }
      loop(x)
    }
  }
}

這個例子建立了一個名為IntWithTimes的隱式類。這個類包含一個int值和一個名為times的方法。要使用這個類,只需將其匯入作用域內並呼叫times方法。比如:

scala>  import Helpers._
import Helpers._
scala> 5 times println("HI")
HI
HI
HI
HI
HI

使用隱式類時,類名必須在當前作用域內可見且無歧義,這一要求與隱式值等其他隱式型別轉換方式類似。

只能在別的trait/類/物件內部定義。

     object Helpers {
        implicit  class RichInt(x: Int)  //  正確!
    }
     implicit  class RichDouble(x: Double)  //  錯誤!

建構函式只能攜帶一個非隱式引數。

  implicit  class RichDate(date: java.util.Date)  //  正確!
  implicit  class Indexer[T](collecton: Seq[T], index: Int)  //  錯誤!
  implicit  class Indexer[T](collecton: Seq[T])( implicit index: Index)  //  正確!

雖然我們可以建立帶有多個非隱式引數的隱式類,但這些類無法用於隱式轉換。

在同一作用域內,不能有任何方法、成員或物件與隱式類同名。

object Bar
implicit  class Bar(x: Int)  //  錯誤!

val x = 5
implicit  class x(y: Int)  //  錯誤!

implicit  case  class Baz(x: Int)  //  錯誤!

5.6.  隱式轉換函式

是指那種以implicit關鍵字宣告的帶有單個引數的函式,這種函式將被自動引用,將值從一種型別轉換成另一種型別。

使用隱含轉換將變數轉換成預期的型別是編譯器最先使用 implicit 的地方。這個規則非常簡單,當編譯器看到型別X而卻需要型別Y,它就在當前作用域查詢是否定義了從型別X到型別Y的隱式定義。

比如,通常情況下,雙精度實數不能直接當整數使用,因為會損失精度:

scala>  val i:Int = 3.5
<console>:7: error:  type mismatch;
 found   : Double(3.5)
 required: Int
        val i:Int = 3.5
                   ^

當然你可以直接呼叫 3.5.toInt。

這裡我們定義一個從 Double 到 Int 的隱含型別轉換的定義,然後再把 3.5 賦值給整數,就不會報錯。

scala>  implicit  def doubleToInt(x:Double) = x toInt
doubleToInt: (x: Double)Int
scala>  val i:Int = 3.5
i: Int = 3

此時編譯器看到一個浮點數 3.5,而當前賦值語句需要一個整數,此時按照一般情況,編譯器會報錯,但在報錯之前,編譯器會搜尋是否定義了從 Double 到 Int 的隱含型別轉換,本例,它找到了一個 doubleToInt。 因此編譯器將把

val i:Int = 3.5

轉換成

val i:Int = doubleToInt(3.5)

這就是一個隱含轉換的例子,但是從浮點數自動轉換成整數並不是一個好的例子,因為會損失精度。 Scala 在需要時會自動把整數轉換成雙精度實數,這是因為在 Scala.Predef 物件中定義了一個

implicit  def int2double(x:Int) :Double = x.toDouble

 Scala.Predef 是自動引入到當前作用域的,因此編譯器在需要時會自動把整數轉換成 Double 型別。

5.7.  隱式引數

object Test{
     trait Adder[T] {
       def add(x:T,y:T):T
    }

     implicit  val a =  new Adder[Int] {
       override  def add(x: Int, y: Int): Int = x+y
    }

     def addTest(x:Int,y:Int)( implicit adder: Adder[Int]) = {
      adder.add(x,y)
    }

   addTest(1,2)       //  正確 , = 3
   addTest(1,2)(a)    //  正確 , = 3
   addTest(1,2)( new Adder[Int] {
       override  def add(x: Int, y: Int): Int = x-y
    })    //  同樣正確 , = -1
}

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913892/viewspace-2660150/,如需轉載,請註明出處,否則將追究法律責任。

相關文章