Scala Implicit Conversion

Scala Implicit Conversion函數

從一個簡單例子出發,咱們定義一個函數接受一個字符串參數,並進行輸出spa

def func(msg: String): Unit = println(msg)

這個函數在func("11")調用時候正常,可是在執行func(11)或func(1.1)時候就會報error: type mismatch的錯誤。這個問題很好解決:scala

  1. 針對特定的參數類型,重載多個func函數,這個不難,傳統JAVA中的思路,可是須要定義多個函數code

  2. 使用超類型,好比使用AnyVal,Any,這樣的話比較麻煩,須要在函數中針對特定的邏輯作類型轉化,從而進一步處理對象

上面兩個方法使用的是傳統JAVA思路,雖然均可以解決該問題,可是缺點是不夠簡潔;在充滿了語法糖的Scala中,針對類型轉換提供了特有的implicit隱式轉換的功能。ci

隱式轉化是一個函數,能夠針對一個變量在須要的時候自動的進行類型轉換;針對上面的例子,咱們能夠定義intToString函數字符串

def func(msg: String): Unit = println(msg)

implicit def intToString(i: Int): String = i.toString

func("hello world!")
func(123)

運行並輸出,get

C:\WorkSpace6-scala\scala-train\src\com\usoft>scala implicit.scalait

warning: there was one feature warning; re-run with -feature for detailsio

one warning found

hello world!

123

此時在調用func(11)時候, scala會自動針對11進行intToString函數的調用, 從而實現能夠在func函數已有的類型上提供了新的類型支持,這裏有幾點要說一下:

  1. 隱式轉換的核心是from類型和to類型,至於函數名稱並不重要;上面咱們取爲intToString,只是爲了直觀,int2str的功能是同樣的;隱式轉換函數只關心from-to類型之間的匹配,好比咱們須要to類型,可是提供了from類型,那麼相應的implicit函數就會調用 

  2. 隱式轉換隻關心類型,因此若是同時定義兩個隱式轉換函數,from/to類型相同,可是函數名稱不一樣,這個時候函數調用過程當中若是須要進行類型轉換,就會報ambiguous二義性的錯誤,即不知道使用哪一個隱式轉換函數進行轉換

上面咱們看到的例子是將函數的參數從一個類型自動轉換爲一個類型的例子,在Scala中,除了針對函數參數類型進行轉換之外,還能夠對函數的調用者的類型進行轉換。

好比A+B,上面咱們談到是針對B進行類型自動轉換, 其實能夠在A上作類型轉換,下面咱們拿一個例子來講明

class IntWritable(_value: Int) {
  var value = _value

  def +(that: IntWritable): IntWritable = {
    new IntWritable(that.value + value)
  }
}

implicit def intToWritable(int: Int) = new IntWritable(int)
new IntWritable(10) + 10

上面咱們首先定義了一個類:IntWritable,併爲int提供了一個隱式類型轉換intToWritable,從而可使得IntWritable的+函數在原先只接受IntWritable類型參數的基礎上,接受一個Int類型的變量進行運算,即new IntWritable(10) + 10能夠正常運行

如今換一個角度將"new IntWritable(10) + 10" 換爲"10 + new IntWritable(10)"會是什麼結果呢?會報錯誤嗎?

以下代碼,

class IntWritable(_value: Int) {
  var value = _value

  def +(that: IntWritable): IntWritable = {
    new IntWritable(that.value + value)
  }
}

implicit def intToWritable(int: Int) = new IntWritable(int)
//implicit def writableToInt(that: IntWritable) = that.value


//val result1 = new IntWritable(10) + 10
val result2 = 10 + new IntWritable(10)

//println(result1.getClass.getName)
println(result2.getClass.getName) //Main$$anon$1$IntWritable

按道理是應該報錯誤,首先一個Int內置類型的+函數,沒有IntWritable這個參數類型;其次,咱們沒有針對 IntWritable 類型提供到 Int 的隱式轉換,

即沒有提供writableToInt的implicit函數。

可是結果是什麼?

10 + new IntWritable(10) 是能夠正常運行的,並且整個表達的類型爲IntWritable,而不是Int,即Int的10被intToWritable函數隱式函數轉換爲IntWritable類型;

結論:隱式轉換能夠針對函數參數類型和函數對象進行類型轉換;如今問題來了,看下面的例子

class IntWritable(_value: Int) {
  var value = _value

  def +(that: IntWritable): IntWritable = {
    new IntWritable(that.value + value)
  }
}

implicit def intToWritable(int: Int) = new IntWritable(int)
implicit def writableToInt(that: IntWritable) = that.value


val result1 = new IntWritable(10) + 10
val result2 = 10 + new IntWritable(10)

println(result1.getClass.getName) //Main$$anon$1$IntWritable
println(result2.getClass.getName) //int

在上面的IntWritable類的基礎上,咱們提供了兩個隱式類型轉換函數,即Int和IntWritable之間的雙向轉換;這樣的狀況下result1和result2兩個變量的類型是什麼?

答案:result1的類型爲IntWritable, result2的類型Int;很好理解, result1中的Int類型的10被intToWritable隱式轉換爲IntWritable;而result2中的IntWritable(10)被writableToInt 隱式轉換爲Int類型;

你確定會問?result2中爲何不是像上面的例子同樣, 把Int類型的10隱式轉換爲IntWritable類型呢?緣由就是隱式轉換的優先級;

發生類型不匹配的函數調用時, scala會嘗試進行類型隱式轉換;首先優先進行函數參數的類型轉換,若是能夠轉換, 那麼就完成函數的執行; 不然嘗試去對函數調用對象的類型進行轉換; 若是兩個嘗試都失敗了,就會報方法不存在或者類型不匹配的錯誤;

OK, Scala的隱式轉換是Scala裏面隨處可見的語法。

================END================

相關文章
相關標籤/搜索