scala implicit (隱式轉換函數(shù)、隱式類灸促、隱式參數(shù)诫欠、隱式值)

implicit 是scala一個關鍵字,使scala更靈活和容易擴展浴栽。

1.隱式轉換函數(shù)

implicit def int2str(x:Int):String = x.toString

2.隱式類

implicit class Box(x: Int) {? ?

}

3.隱式參數(shù)

def compare[T](x:T,y:T)(implicit ordered: Ordering[T]):Int = {

? ordered.compare(x,y)

}

4.隱式值

implicit val x: Int = 0

5.隱式對象

implicit object obj {

}

6.context bound

def compare2[T: Ordering](x: T, y: T) = {

? val ord = implicitly[Ordering[T]]

? ord.compare(x, y)

}

一荒叼、隱式轉換函數(shù)、Overview

首先來看隱式轉換函數(shù)典鸡。

implicit def int2str(x:Int):String = x.toString1

聲明了一個隱式函數(shù)int2str被廓。它告訴編譯器,這個函數(shù)是一個隱式轉換函數(shù)萝玷,把Int類型的值轉換String類型的值嫁乘。

如果在進行一個對Int類型的操作時不合法,編譯器會在當前作用域尋找合適的隱式轉換球碉,來嘗試使這種操作合法亦渗。隱式轉換發(fā)生在這兩種情景:

e是一個S類型的表達式,而需要的卻是T類型汁尺,編譯器會尋找S=>T的隱式轉換

e是一個S類型的表達式法精,使用點號訪問e.m時,m不是類型S的成員痴突,編譯器會尋找合適的隱式轉換使e.m合法

隱式轉換的用途就是擴展已有的類搂蜓,在不修改原有類的基礎上為其添加新的方法、成員辽装。

10.concat("hello")10.length

接受String類型的函數(shù)也可以接受Int類型:

def hi(x:String) = println("hi"+x)

hi(123)

需要注意:?

1.對于隱式轉換函數(shù)帮碰,編譯器最關心的是它的類型簽名,即它將哪一種類型轉換到另一種類型拾积,也就是說它應該接受只一個參數(shù)殉挽,對于接受多參數(shù)的隱式函數(shù)來說就沒有隱式轉換的功能了丰涉。

implicit def int2str(x:Int):String = x.toString // 正確implicit def int2str(x:Int,y:Int):String = x.toString // 錯誤

2.不支持嵌套的隱式轉換

class A{

def hi = println("hi")

}

implicit def int2str(x:Int):String = x.toString

implicit def str2A(x:Int,y:Int):A = new A"str".hi? // 正確1.hi? ? ? // 錯誤

3.不能存在二義性,即同一個作用域不能定義兩個相同類型的隱式轉換函數(shù)斯碌,這樣編譯器將無法決定使用哪個轉換

/* 錯誤-- */implicit def int2str(x:Int):String = x.toString

implicit def anotherInt2str(x:Int):A = x.toString/* --錯誤 */

4.代碼能夠在不使用隱式轉換的前提下能編譯通過一死,就不會進行隱式轉換

二、隱式類

前面提到傻唾,隱式轉換最重要的應用是擴展已存在的類投慈,它的功能和c#中的擴展方法很類似。比如我們想對已有的Int類型添加一個sayhi的方法冠骄,可以這樣做:

class SayhiImpl(ivalue:Int) {

? val value:Int = ivalue

? def sayhi = println(s"Hi $value!")

}

implicit def int2Sayhi(x:Int) = new SayhiImpl(x)

那么調用123.sayhi伪煤,將會輸出:Hi 123!。

即我們先實現(xiàn)一個支持sayhi方法的類凛辣,再寫一個隱式轉換函數(shù)抱既,使得Int類也支持sayhi。但是這種寫法過于啰嗦了扁誓,可以使用隱式類實現(xiàn)等價的功能:

implicit class SayhiImpl(ivalue:Int) {

? val value:Int = ivalue

? def sayhi = println(s"Hi $value!")

}123.sayhi? //合法

隱式類就是在類定義前加一個implicit關鍵字防泵,這表示它的構造函數(shù)是一個隱式轉換函數(shù),能夠將參數(shù)的類型轉換成自己的類型跋理,在這里就是構造函數(shù)SayhiImpl(ivalue:Int)定義了Int到SayhiImpl的隱式轉換。

在使用隱式類時需要注意以下限制條件

1.只能在別的trait/類/對象內部定義恬总。

? ? object Helpers {

? ? ? implicit class RichInt(x: Int) // 正確前普!

? ? }

? ? implicit class RichDouble(x: Double) // 錯誤!

2.構造函數(shù)只能攜帶一個非隱式參數(shù)壹堰。

implicit class RichDate(date: java.util.Date) // 正確拭卿!

implicit class Indexer[T](collecton: Seq[T], index: Int) // 錯誤!

implicit class Indexer[T](collecton: Seq[T])(implicit index: Index) // 正確

雖然我們可以創(chuàng)建帶有多個非隱式參數(shù)的隱式類贱纠,但這些類無法用于隱式轉換峻厚。

3.implict關鍵字不能用于case類

implicit case class Baz(x: Int) // 錯誤!

三谆焊、隱式參數(shù)

看最開始的例子:

def compare[T](x:T,y:T)(implicit ordered: Ordering[T]):Int = {

? ordered.compare(x,y)

}

在函數(shù)定義的時候惠桃,支持在最后一組參數(shù)使用?implicit,表明這是一組隱式參數(shù)辖试。在調用該函數(shù)的時候辜王,可以不用傳遞隱式參數(shù),而編譯器會自動尋找一個implict標記過的合適的值作為該參數(shù)罐孝。

例如上面的函數(shù)呐馆,調用compare時不需要顯式提供ordered,而只需要直接compare(1,2)這樣使用即可莲兢。

再舉一個例子:

object Test{

? ? trait Adder[T] {

? ? ? def add(x:T,y:T):T

? ? }

? ? implicit val a = new Adder[Int] {

? ? ? override def add(x: Int, y: Int): Int = x+y

? ? }

? ? def addTest(x:Int,y:Int)(implicit adder: Adder[Int]) = {

? ? ? adder.add(x,y)

? ? }

? addTest(1,2)? ? ? // 正確, = 3? addTest(1,2)(a)? // 正確, = 3? addTest(1,2)(new Adder[Int] {

? ? ? override def add(x: Int, y: Int): Int = x-y

? ? })? // 同樣正確, = -1}

Adder是一個trait汹来,它定義了add抽象方法要求子類必須實現(xiàn)续膳。

addTest函數(shù)擁有一個Adder[Int]類型的隱式參數(shù)。

在當前作用域里存在一個Adder[Int]類型的隱式值implicit val a收班。

在調用addTest時坟岔,編譯器可以找到implicit標記過的a,所以我們不必傳遞隱式參數(shù)而是直接調用addTest(1,2)闺阱。而如果你想要傳遞隱式參數(shù)的話炮车,你也可以自定義一個傳給它,像后兩個調用所做的一樣酣溃。

四瘦穆、隱式值和隱式對象

最開始的示例代碼有,

隱式值:

implicit val x: Int = 0

隱式對象:

implicit object obj {

}

上面提到過赊豌,在調用含有隱式參數(shù)的函數(shù)時扛或,編譯器會自動尋找合適的隱式值當做隱式參數(shù),而只有用implict標記過的值碘饼、對象熙兔、函數(shù)才能被找到。

例如自動尋找隱式對象:

? ? implicit object Obj {

? ? ? def hello(s:String) = println(s"Hello $s!")

? ? }

? ? def test2(s:String)(implicit o: Obj.type ) = {

? ? ? o.hello(s)

? ? }

? test2("world")? // 輸出Hello world!

自動尋找隱式函數(shù):

? ? implicit def int2str(x: Int): String = x.toString

? ? def test1(x: Int, func: String => Unit)

? ? ? ? ? ? (implicit helper: Int => String) = {

? ? ? func("\"" + helper(x) + "\"")

? ? }

? ? test1(12, println) // 打印出"12"

五艾恼、context bound

最后來看隱式作為泛型類型的限制:

def compare2[T: Ordering](x: T, y: T) = {

? val ord = implicitly[Ordering[T]]

? ord.compare(x, y)

}

上面compare2是一個泛型函數(shù)住涉,其有一個類型參數(shù)T,在這里T:Ordering對T類型做出了限制钠绍,要求必須存在一個Ordering[T]類型的隱式值舆声,這種限制就叫做context bound。

這其實是隱式參數(shù)的語法糖柳爽,它等價于:

def compare2[T](x: T, y: T)(implicit ord:Ordering[T]) = {

? ord.compare(x, y)

}

注意到前面的函數(shù)體里用到了implicitly函數(shù)媳握。這是因為在使用[T: Ordering]這樣的類型限制時,我們沒有能接觸到具體的Ordering[T]類型的隱式值ord磷脯,這時候調用implicitly函數(shù)蛾找,就可以拿到這個隱式值,進而進行下一步操作了赵誓。沒有這個函數(shù)的話打毛,你需要這樣寫:

def compare2[T: Ordering](x: T, y: T) = {

? def helper(implicit ord:Ordering[T]) == ord.compare(x, y)

? helper

}

隱式解析機制

最后加一點比較深入一點的內容,看一下隱式值的尋找機制

即編譯器是如何查找到缺失信息的俩功,解析具有以下兩種規(guī)則:

1 .首先會在當前代碼作用域下查找隱式實體(隱式方法 隱式類 隱式對象)?

2.如果第一條規(guī)則查找隱式實體失敗隘冲,會繼續(xù)在隱式參數(shù)的類型的作用域里查找

類型的作用域是指與該類型相關聯(lián)的全部伴生模塊,一個隱式實體的類型T它的查找范圍如下:

(1)如果T被定義為T with A with B with C,那么A,B,C都是T的部分绑雄,在T的隱式解析過程中展辞,它們的伴生對象都會被搜索

(2)如果T是參數(shù)化類型,那么類型參數(shù)和與類型參數(shù)相關聯(lián)的部分都算作T的部分万牺,比如List[String]的隱式搜索會搜索List的?

伴生對象和String的伴生對象

(3) 如果T是一個單例類型p.T罗珍,即T是屬于某個p對象內洽腺,那么這個p對象也會被搜索

(4) 如果T是個類型注入S#T,那么S和T都會被搜索

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末覆旱,一起剝皮案震驚了整個濱河市蘸朋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扣唱,老刑警劉巖藕坯,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異噪沙,居然都是意外死亡炼彪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進店門正歼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辐马,“玉大人,你說我怎么就攤上這事局义∠惨” “怎么了?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵萄唇,是天一觀的道長檩帐。 經(jīng)常有香客問我,道長另萤,這世上最難降的妖魔是什么湃密? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮仲墨,結果婚禮上勾缭,老公的妹妹穿的比我還像新娘揍障。我一直安慰自己目养,他們只是感情好,可當我...
    茶點故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布毒嫡。 她就那樣靜靜地躺著癌蚁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兜畸。 梳的紋絲不亂的頭發(fā)上努释,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天,我揣著相機與錄音咬摇,去河邊找鬼伐蒂。 笑死,一個胖子當著我的面吹牛肛鹏,可吹牛的內容都是我干的逸邦。 我是一名探鬼主播恩沛,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼缕减!你這毒婦竟也來了雷客?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤桥狡,失蹤者是張志新(化名)和其女友劉穎搅裙,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體裹芝,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡部逮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了局雄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甥啄。...
    茶點故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖炬搭,靈堂內的尸體忽然破棺而出蜈漓,到底是詐尸還是另有隱情,我是刑警寧澤宫盔,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布融虽,位于F島的核電站,受9級特大地震影響灼芭,放射性物質發(fā)生泄漏有额。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一彼绷、第九天 我趴在偏房一處隱蔽的房頂上張望巍佑。 院中可真熱鬧,春花似錦寄悯、人聲如沸萤衰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脆栋。三九已至,卻和暖如春洒擦,著一層夾襖步出監(jiān)牢的瞬間椿争,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工熟嫩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留秦踪,地道東北人。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像椅邓,于是被迫代替她去往敵國和親舍扰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,576評論 2 349