轉(zhuǎn)自:
scala implicit 關(guān)鍵字用法總結(jié)
Scala implicit 使用簡介
implicit關(guān)鍵字是scala中一個比較有特點的關(guān)鍵字下梢,他保證了scala在很多時候沒有一些不必要的代碼冗余牍帚,使得scala看起來更加整潔恭应,同時使得scala的一些庫在設(shè)計的時候沧烈,可以有更加直觀的操作方法。
隱式參數(shù)
隱式參數(shù)(implicit parameters)是最簡單的形態(tài)喂击,也是最容易理解的剂癌。與函數(shù)中增加默認值的效果類似,但是更為靈活翰绊。
object ImplicitParameters{
implicit val name: String = "default" // name名稱可與下面參數(shù)名稱不同佩谷,他是根據(jù)變量類型進行推導(dǎo)替換的
log("init")
def log(msg:String)(implicit name:String): Unit = { println(s"[$name] $msg ")}
def process(): Unit = {
implicit val name = "process"
log("doing something")
}
def main(args: Array[String]): Unit = {
implicit val name = "main"
log("start")
process()
log("end")("custom name")
}
}
// 輸出如下
[default] init
[main] start
[process] doing something
[custom name] end
在運行時,函數(shù)的隱式參數(shù)會自動去尋找符合條件類型的隱式變量监嗜,然后進行變量替換谐檀。需要注意的是,想同類型的隱式變量只能定義一個裁奇,不然會報錯桐猬,編譯器解析的時候不知道該替換哪一個。
隱式參數(shù)有三種定義方法:
-
def func(implicit x: Int)
, x是implicit的框喳; -
def func(x: Int)(implicit y:Int)
, y是implicit的课幕; -
def func(implicit x:Int, y:Int)
, x, y都是implicit的;
以下定義方式是錯誤的五垮,無法通過:
def err(x: Int, implicit y:Int)
def err(implicit x:Int)(implicit y:Int)
def err(implicit x:Int)(y:Int)
隱式函數(shù)
隱式函數(shù)用于類型轉(zhuǎn)換乍惊。如當(dāng)函數(shù)輸入類型與實際要求類型不符合時,編譯器會自動搜尋可用的隱式函數(shù)對數(shù)據(jù)進行類型轉(zhuǎn)換放仗,使得程序正常運行润绎。相當(dāng)于給了一個修復(fù)編譯器調(diào)用錯誤的機會,同時也賦予了強大的擴展能力,可以更為方便地實現(xiàn) DSL莉撇。
import scala.language.implicitConversions
//這個import是為了避免出現(xiàn)warning
object Test extends App {
implicit def conv(a: Int) = {
println("in conv")
a.toString
}
def say(b: String) = println(b)
say(5)
}
//輸出結(jié)果:
// in conv
// 5
//這說明過程是say(conv(5))
//原因是編譯器在檢查的時候發(fā)現(xiàn)需要一個String類型的參數(shù)呢蛤,但是代入的是一個Int,于是
//他會在范圍內(nèi)尋找implicit的function棍郎,找到了符合這個要求的String => Int的function其障,然后調(diào)用
隱式類
implicit class MyClass(x: Int)
隱式類是隱式函數(shù)的一個語法糖(一個不恰當(dāng)?shù)谋扔魇荘ython的裝飾器函數(shù)和裝飾器類)。其作用主要是類的主構(gòu)造函數(shù)可以作為隱式轉(zhuǎn)換的參數(shù)涂佃,相當(dāng)于其主構(gòu)造函數(shù)可以用來當(dāng)做一個implicit的function励翼,如下例:
object Test{
implicit class MyName(x:Int){
println("I'm in cons")
val y = x
}
def say(x:MyName) = {
println(x.y)
}
say(5)
}
# 輸出結(jié)果
I'm in cons
5
這里的MyName是一個隱式類,其主構(gòu)造函數(shù)可以用作隱式轉(zhuǎn)換辜荠,所以say需要一個MyName類型的參數(shù)汽抚,但是調(diào)用的時候給的是一個Int,這里就會調(diào)用MyName的主構(gòu)造函數(shù)轉(zhuǎn)換為一個MyName的對象伯病,然后在println其y的值造烁。
隱式轉(zhuǎn)換規(guī)則
1、標(biāo)記規(guī)則:隱式轉(zhuǎn)換中涉及到的變量午笛、函數(shù)惭蟋、類都是帶有implicit關(guān)鍵詞的,也就是說隱式轉(zhuǎn)換必須有implicit定義才能生效季研;
2敞葛、作用域規(guī)則:在隱式轉(zhuǎn)換使用的位置必須是單標(biāo)識符可見的,也就是可以無前綴引用与涡,例如惹谐,可以是x,但不可以是foo.x驼卖;
3氨肌、作用域規(guī)則延伸:作用域規(guī)則未找到的情況下,會在類型的隱式作用域(伴生對象)內(nèi)查找酌畜,吟詩作用域是指與該類型相關(guān)聯(lián)的全部伴生模塊怎囚;
4、代碼優(yōu)先規(guī)則:隱式轉(zhuǎn)換觸發(fā)的時機是在編譯器出現(xiàn)了查找方法失敗的情況下才會被觸發(fā)桥胞,因此如果代碼可以正常執(zhí)行的話恳守,是不會觸發(fā)隱式轉(zhuǎn)換的;
5贩虾、有且只有一次隱式轉(zhuǎn)換規(guī)則:觸發(fā)一次隱式轉(zhuǎn)換催烘,只能轉(zhuǎn)換一次。如x+y的x觸發(fā)隱式轉(zhuǎn)換缎罢,只會被隱式轉(zhuǎn)換為convert(x)+y伊群,而不會進行兩次隱式轉(zhuǎn)換convert2(convert1(x))+y考杉。
需注意的點:
1、在使用的時候舰始,不要再一個作用域內(nèi)不要定義多個相同類型的隱式變量崇棠,因為隱式變量是根據(jù)類型匹配,所以定義多個相同類型的隱式變量丸卷,會報編譯錯誤枕稀,編譯器無法進行選擇。另外谜嫉,隱式變量本身也是可以在作用域內(nèi)使用和變量一樣的遮蔽(shadow)抽莱。實踐中,建議新建特定的只包含一個變量的類骄恶,來保證可以明確地進行預(yù)期的隱式轉(zhuǎn)換,不會被其他不經(jīng)意的代碼 shadow匕垫;
2僧鲁、在使用隱式函數(shù)的時候,請 import scala.language.implicitConversions象泵,否則編譯的時候會有一個警告:warning: there was one feature warning; re-run with -feature for details寞秃。