隱式轉(zhuǎn)換
隱式轉(zhuǎn)換是使用implicit修飾的帶有單個參數(shù)的普通函數(shù)。這種函數(shù)將自動應(yīng)用猫十,將值從一種類型轉(zhuǎn)換為另一種類型然爆。
舉例說明:
我們想將整數(shù)n轉(zhuǎn)換為分?jǐn)?shù)n/1,
定義implicit def int2Fraction(n: Int) = Fraction(n, 1)
在進(jìn)行如下表達(dá)式求值的時候:
val result = 3 * Fraction(4, 5)
編譯器將調(diào)用int2Fraction(3)浦妄,將整數(shù)轉(zhuǎn)換成一個Fraction對象,然后按照Fraction類的*方法定義來進(jìn)行計算见芹。
隱式轉(zhuǎn)換可以為現(xiàn)有的類庫添加功能:
我們想為java.io.File類添加一個read方法來讀取文件
在Scala中校辩,可以定義一個類型來提供read或你想要的功能:
class RichFile(val from: File) {
def read = Source.fromFile(from.getPath).mkString
}
然后再提供一個隱式轉(zhuǎn)換來將原來的File類型轉(zhuǎn)換到新定義的類型:
implicit def file2RichFile(from: File) = new RichFile(from)
這樣就可以在File對象上調(diào)用read方法了,它被隱式轉(zhuǎn)換為一個RichFile對象辆童。
隱式轉(zhuǎn)換的規(guī)則
作用域規(guī)則
隱式轉(zhuǎn)換函數(shù)的可用位置:
- 位于源或目標(biāo)類型的伴生對象中的隱式函數(shù)
- 位于當(dāng)前作用域可以以單個標(biāo)識符指代的隱式函數(shù)
假如隱式函數(shù)放在了Conversions對象中,而這個對象位于com.xxx.yyy包惠赫,那么引入語句應(yīng)該是:import com.xxx.yyy.Conversions._
把鉴。
無歧義規(guī)則
隱式轉(zhuǎn)換的應(yīng)用場景:
- 當(dāng)表達(dá)式的類型與預(yù)期的類型不同時:
比如sqrt(Fraction(1, 4))
中,sqrt期望一個Double的參數(shù)儿咱,編譯器會調(diào)用類似fraction2Double的隱式函數(shù)來將Fraction對象轉(zhuǎn)換為Double對象庭砍。
- 當(dāng)對象訪問一個不存在的成員時:
比如new File("README").read
中,F(xiàn)ile沒有read方法混埠,編譯器會調(diào)用file2RichFile怠缸,然后調(diào)用RichFile中定義的read方法。 - 當(dāng)對象調(diào)用某個方法钳宪,而該方法的參數(shù)聲明與傳入?yún)?shù)不匹配時:
比如3 * Fraction(4, 5)
中揭北,Int類的*方法不接收Fraction作為參數(shù),編譯器會調(diào)用int2Fraction進(jìn)行隱式轉(zhuǎn)換吏颖。
編譯器不會使用隱式轉(zhuǎn)換的情況:
- 如果代碼能夠不適用隱式轉(zhuǎn)換的前提下通過編譯搔体,則不會使用隱式轉(zhuǎn)換。
- 編譯器不會嘗試同時執(zhí)行多個轉(zhuǎn)換半醉,所以隱式轉(zhuǎn)換是單一調(diào)用的疚俱。
- 存在二義性的轉(zhuǎn)換是錯誤的,編譯器將會報錯缩多。
隱式參數(shù)
函數(shù)或方法可以帶有一個標(biāo)記為implicit的參數(shù)列表呆奕。該情況下,編譯器會查找缺省值衬吆,提供給該函數(shù)或方法梁钾。
比如:
case class Delimiters(left: String, right: String)
def quote(what: String)(implicit delims: Delimiters) =
delims.left + what + delims.right
implicit val quoteDelimiters = Delimiters("<<", ">>")
當(dāng)我們調(diào)用quote("Scala Programming")
時,編譯器會查找一個類型為Delimiters的隱式值逊抡,輸出<<Scala Programming>>
陈轿。
編譯器的查找位置:
- 當(dāng)前作用域所有可用單個標(biāo)識符指代的滿足類型要求的val和def
- 與所要求類型相關(guān)聯(lián)的類型的繁盛對象。相關(guān)聯(lián)的類型包括所要求類型本身,以及它的類型參數(shù)麦射。
利用隱式參數(shù)進(jìn)行隱式轉(zhuǎn)換
我們提供一個泛型函數(shù)來得到相對小的值:
def smaller[T](a: T, b: T) = if (a < b) a else b
這里由于我們并不知道a和b的類型是否有<操作符蛾娶,所以編譯器不會通過。
解決辦法是添加一個隱式參數(shù)order來指代一個轉(zhuǎn)換函數(shù):
def smaller(a: T, b: T)(implicit order: T => Ordered[T])
= if(order(a) < b) a else b
由于Ordered[T]特質(zhì)中有一個接受T作為參數(shù)的<方法潜秋,所以編譯器將在編譯時知道T蛔琅,并且從而判決是否T => Ordered[T]類型的隱式定義存在于作用域中。
這樣峻呛,才可以調(diào)用smaller(40, 2)
或者smaller("AA", "BB")
罗售。
注意,order是一個帶有單個參數(shù)的函數(shù)钩述,被打上了implicit標(biāo)簽寨躁,所以它不僅是一個隱式參數(shù),也是一個隱式轉(zhuǎn)換牙勘。那么职恳,我們可以在函數(shù)體重省略order的顯示調(diào)用。
def smaller[T](a: T, b: T)(implicit order: T => Ordered[T])
= if (a < b) a else b
因為a沒有帶<的方法方面,那么會調(diào)用order(a)進(jìn)行轉(zhuǎn)換放钦。
轉(zhuǎn)載請注明作者Jason Ding及其出處
jasonding.top
Github博客主頁(http://blog.jasonding.top/)
CSDN博客(http://blog.csdn.net/jasonding1354)
簡書主頁(http://www.reibang.com/users/2bd9b48f6ea8/latest_articles)
Google搜索jasonding1354進(jìn)入我的博客主頁