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

使用 implicits 的一些規(guī)則

在 Scala 中的 implicit 定義指編譯器在需要修復類型匹配時可以用來自動插入的定義。比如說,如果 x+y 類型不匹配,那么編譯器可能試著使用 convert(x) + y, 其中 convert 由某個 implicit 定義的,這有點類似一個整數(shù)和一個浮點數(shù)相加,編譯器可以自動把整數(shù)轉(zhuǎn)換為浮點數(shù)。Scala 的 implicit 定義是對這種情況的一個推廣,你可以定義一個類型在需要時,如何自動轉(zhuǎn)換成另外一種類型。

Scala 的 implicit 定義符合下面一些規(guī)則:

標記規(guī)則

只有哪些使用 implicit 關(guān)鍵字的定義才是可以使用的隱式定義。關(guān)鍵字 implicit 用來標記一個隱式定義。編譯器才可以選擇它作為隱式變化的候選項。你可以使用 implicit 來標記任意變量,函數(shù)或是對象。

例如下面為一個隱式函數(shù)定義:

implicit def intToString(x:Int) : x.toString

編譯器只有在 convert 被標記成 implicit 才會將 x + y 改成convert(x) + y 。當然這是在 x + y 類型不匹配時。

范圍規(guī)則

編譯器在選擇備選 implicit 定義時,只會選取當前作用域的定義,比如說編譯器不會去調(diào)用 someVariable.convert。如果你需要使用 someVariable.convert,你必須把 someVarible 引入到當前作用域。也就是說編譯器在選擇備選 implicit 時,只有當 convert 是當前作用域下單個標志符時才會作為備選 implicit。比如說,對于一個函數(shù)庫來說,在一個 Preamble 對象中定義一些常用的隱式類型轉(zhuǎn)換非常常見,因此需要使用 Preamble 的代碼可以使用 “import Preamble._” 把這些 implicit 定義引入到當前作用域才可以。

這個規(guī)則有一個例外,編譯器也會在類的伙伴對象定義中查找所需的 implicit 定義。例如下面的定義:

object Dollar {
    implicit def dollarToEuro(x:Dollar):Euro = ...
    ...
}
class Dollar {
   ...
}

如果在 class Dollar 的方法有需要 Euro 類型,但輸入數(shù)據(jù)使用的是 Dollar,編譯器會在其伙伴對象 object Dollar 查找所需的隱式類型轉(zhuǎn)換,本例定義一個從 Dollar 到 Euro 的 implicit 定義可以使用。

一次規(guī)則

編譯器在需要使用 implicit 定義時,只會試圖轉(zhuǎn)換一次,也就是編譯器永遠不會把 x + y 改寫成 convert1(convert2(x)) + y。

優(yōu)先規(guī)則

編譯器不會在 x+y 已經(jīng)是合法的情況下去調(diào)用 implicit 規(guī)則。

命名規(guī)則

你可以為 implicit 定義任意的名稱。通常情況下你可以任意命名,implicit 的名稱只在兩種情況下有用:一是你想在一個方法中明確指明,另外一個是想把那一個引入到當前作用域。比如我們定義一個對象,包含兩個 implicit定義:

object MyConversions {
    implicit def stringWrapper(s:String):IndexedSeq[Char] = ...
    implicit def intToString(x:Int):String = ...
}

在你的應用中,你想使用 stringWrapper 變換,而不想把整數(shù)自動轉(zhuǎn)換成字符串,你可以只引入 stringWrapper。

import  MyConversions.stringWrapper

編譯器使用 implicit 的幾種情況

有三種情況使用 implicit: 一是轉(zhuǎn)換成預期的數(shù)據(jù)類型,而是轉(zhuǎn)換 selection 的 receiver,三是隱含參數(shù)。轉(zhuǎn)換成預期的數(shù)據(jù)類型比如你有一個方法參數(shù)類型是 IndexedSeq[Char],在你傳入 String 時,編譯器發(fā)現(xiàn)類型不匹配,就檢查當前作用域是否有從 String 到 IndexedSeq 隱式轉(zhuǎn)換。

轉(zhuǎn)換 selection 的 receiver 允許你適應某些方法調(diào)用,比如 “abc”.exist ,”abc”類型為 String,本身沒有定義 exist 方法,這時編輯器就檢查當前作用域內(nèi) String 的隱式轉(zhuǎn)換后的類型是否有 exist 方法,發(fā)現(xiàn) stringWrapper 轉(zhuǎn)換后成 IndexedSeq 類型后,可以有 exist 方法,這個和 C# 靜態(tài)擴展方法功能類似。

隱含參數(shù)有點類似是缺省參數(shù),如果在調(diào)用方法時沒有提供某個參數(shù),編譯器會查找當前作用域是否有符合條件的 implicit 對象作為參數(shù)傳入(有點類似 dependency injection)。