鍍金池/ 教程/ Scala/ 轉換被方法調用的對象
轉換被方法調用的對象
View 限定
概述
當有多個隱含轉換可以選擇時
隱含參數(二)
隱含參數(一)
使用 implicits 的一些規(guī)則
隱含類型轉換

轉換被方法調用的對象

隱式變換也可以轉換調用方法的對象,比如但編譯器看到X .method,而類型 X 沒有定義 method(包括基類)方法,那么編譯器就查找作用域內定義的從 X 到其它對象的類型轉換,比如 Y,而類型Y定義了 method 方法,編譯器就首先使用隱含類型轉換把 X 轉換成 Y,然后調用 Y 的 method。

下面我們看看這種用法的兩個典型用法:

支持新的類型

這里我們使用前面例子 Scala開發(fā)教程(50): Ordered Trait 中定義的 Rational 類型為例:

class Rational (n:Int, d:Int) {
    require(d!=0)
    private val g =gcd (n.abs,d.abs)
    val numer =n/g
    val denom =d/g
    override def toString = numer + "/" +denom
    def +(that:Rational)  =
      new Rational(
        numer * that.denom + that.numer* denom,
        denom * that.denom
      )
    def +(i:Int) :Rational =
        new Rational(numer +1*denom,denom)
    def * (that:Rational) =
      new Rational( numer * that.numer, denom * that.denom)
    def this(n:Int) = this(n,1)
    private def gcd(a:Int,b:Int):Int =
      if(b==0) a else gcd(b, a % b)
}

類 Rational 重載了兩個+運算,參數類型分別為 Rational 和 Int。因此你可以把 Rational 和 Rational 相加,也可以把 Rational 和整數相加。

scala> val oneHalf = new Rational(1,2)
oneHalf: Rational = 1/2
scala> oneHalf + oneHalf
res0: Rational = 1/1
scala> oneHalf + 1
res1: Rational = 3/2

但是我們如果使用 1+ oneHalf 會出現什么問題呢?

scala> 1 + oneHalf
<console>:10: error: overloaded method value + with alternatives:
  (x: Double)Double <and>
  (x: Float)Float <and>
  (x: Long)Long <and>
  (x: Int)Int <and>
  (x: Char)Int <and>
  (x: Short)Int <and>
  (x: Byte)Int <and>
  (x: String)String
 cannot be applied to (Rational)
              1 + oneHalf
                ^

整數和其相關類型都沒定義和 Rational 類型相加的操作,因此編譯器報錯,此時編譯器在1能夠轉換成 Rational 類型才可以編譯過,因此我們可以定義一個從整數到 Rational 的隱含類型變換:

scala> implicit def int2Rational(x:Int) = new Rational(x)
int2Rational: (x: Int)Rational

現在再執(zhí)行 1+oneHalf:

scala> 1 + oneHalf
res3: Rational = 3/2

在定義了 int2Rational 之后,編譯器看到 1+oneHalf,發(fā)現 1 沒有定義和 Rational 相加的操作,通常需要報錯,編譯器在報錯之前查找當前作用域從 Int 到其他類型的定義,而這個轉換定義了支持和 Rational 相加的操作,本例發(fā)現 int2Rational,因此編譯器將 1+ oneHalf 轉換為

int2Rational(1)+oneHalf

模擬新的語法結構

隱式轉換可以用來擴展 Scala 語言,定義新的語法結構,比如我們在定義一個 Map 對象時可以使用如下語法:

Map(1 -> "One", 2->"Two",3->"Three")

你有沒有想過->內部是如何實現的,->不是 scala 本身的語法,而是類型 ArrowAssoc 的一個方法。這個類型定義在包 Scala.Predef 對象中。 Scala.Predef 自動引入到當前作用域,在這個對象中,同時定義了一個從類型 Any 到 ArrowAssoc 的隱含轉換。因此當使用 1 -> “One”時,編譯器自動插入從 1 轉換到 ArrowAsso c轉換。具體定義可以參考 Scala 源碼。

利用這種特性,你可以定義新的語法結構,比如行業(yè)特定語言(DSL)。