天天看點

Scala系列之隐式轉換和隐式參數

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

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

隻能在别的trait/類/對象内部定義。

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

構造函數隻能攜帶一個非隐式參數。

implicit class RichDate(date: java.util.Date) // 正确!

implicit class Indexer

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

: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

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           

繼續閱讀