深度剖析scala中的隱式轉(zhuǎn)換

“道常無(wú)為若治,而無(wú)不為耻警。
侯王若能守之蛀缝,萬(wàn)物將自化用含。
化而欲作订晌,吾將鎮(zhèn)之以無(wú)名之樸。
無(wú)名之樸瞻润,夫亦將不欲搏熄。
不欲以靜晦毙,天下將自定谱秽∏⒅”[1]

對(duì)于scala的學(xué)習(xí)成本摹迷,大多會(huì)體現(xiàn)在對(duì)于隱式轉(zhuǎn)換的理解應(yīng)用上疟赊。隱式轉(zhuǎn)換伴隨著泛型,scala所有的神秘之處峡碉、優(yōu)雅之處皆因于此近哟。

隱式在scala中,應(yīng)用場(chǎng)景會(huì)在如下四個(gè)方面:

  • 隱式參數(shù)
scala> def razor(implicit name:String) = name
razor: (implicit name: String)String

直接調(diào)用razor方法

scala> razor
<console>:13: error: could not find implicit value for parameter name: String
       razor
       ^

報(bào)錯(cuò)鲫寄!編譯器說(shuō)無(wú)法為參數(shù)name找到一個(gè)隱式值
定義一個(gè)隱式值后再調(diào)用razor方法

scala> implicit val iname = "Philips"
iname: String = Philips

scala> razor
res12: String = Philips

因?yàn)閷name變量標(biāo)記為implicit吉执,所以編譯器會(huì)在方法省略隱式參數(shù)的情況下去搜索作用域內(nèi)的隱式值作為缺少參數(shù)。
但是如果此時(shí)你又重復(fù)定義一個(gè)隱式變量地来,再次調(diào)用方法時(shí)就會(huì)報(bào)錯(cuò)

scala> implicit val iname2 = "Gillette"
iname2: String = Gillette

scala> razor
<console>:15: error: ambiguous implicit values:
 both value iname of type => String
 and value iname2 of type => String
 match expected type String
       razor
       ^

匹配失敗戳玫,所以隱式轉(zhuǎn)換必須滿(mǎn)足無(wú)歧義規(guī)則,在聲明隱式參數(shù)的類(lèi)型是最好使用特別的或自定義的數(shù)據(jù)類(lèi)型未斑,不要使用Int,String這些常用類(lèi)型咕宿,避免碰巧匹配。

  • 隱式函數(shù)
class Person(val name:String)

class Engineer(val name:String,val salary:Double){
  def mySalary(): Unit ={
     println(name+",your monthly salary is is:"+salary)
  }
}

object Test {
  def main(args: Array[String]): Unit = {
    new Person("sutong").mySalary
  }
}

實(shí)例化一個(gè)person對(duì)象,然后調(diào)用mySalary方法時(shí)府阀,此時(shí)編譯是通不過(guò)的缆镣,因?yàn)閜erson類(lèi)沒(méi)有mySalary方法。但是如果我們加入一個(gè)隱式函數(shù)就能時(shí)編譯通過(guò)并能順利運(yùn)行试浙,如下:

object Test {
  implicit def person2Engineer(p:Person):Engineer = {
    new Engineer(p.name,100000)
  }
  def main(args: Array[String]): Unit = {
    new Person("sutong").mySalary
  }
}

輸出:

sutong,your salary is:100000.0

Scala會(huì)在報(bào)錯(cuò)前董瞻,去找隱式函數(shù),例如我們定義的隱式函數(shù)田巴,根據(jù)函數(shù)簽名钠糊,主要是輸入?yún)?shù),找到了person2Engineer方法固额,并利用此方法將person對(duì)象轉(zhuǎn)換為了Engineer對(duì)象眠蚂,然后發(fā)現(xiàn)Engineer對(duì)象有mySalary方法,直接調(diào)用其mySalary方法斗躏,打印輸出逝慧。運(yùn)行后,Person對(duì)象和Engineer類(lèi)型無(wú)任何關(guān)系啄糙。
約定:

雖然在定義implicit函數(shù)時(shí)scala并未要求要寫(xiě)明返回類(lèi)型笛臣,但我們應(yīng)寫(xiě)明返回類(lèi)型,這樣有助于代碼閱讀隧饼。

  • 隱式對(duì)象
    用implicit修飾的object就是隱式對(duì)象沈堡。
abstract class Template[T]{
  def add(a:T,b:T):T
}

abstract class SubTemplate[T] extends Template[T] {
  def unit:T
}

object Implicit_Object_Test {
  def main(args: Array[String]): Unit = {
    implicit object StringAdd extends SubTemplate[String]{
      override def unit: String = ""
      override def add(a:String,b:String):String = a concat b
    }
    implicit object IntAdd extends SubTemplate[Int]{
      override def unit: Int = 0
      override def add(a:Int,b:Int):Int = a+b
    }

    def sum[T](x:List[T])(implicit m:SubTemplate[T]):T = {
      if(x.isEmpty) m.unit
      else m.add(x.head,sum(x.tail))
    }

    println(sum(List(2,4,6,8,10)))
    println(sum(List("a","b","c")))
  }
}
輸出:
30
abc

首先定義了兩個(gè)含有泛型的抽象類(lèi),Template是父類(lèi)燕雁,而SubTempalte是子類(lèi)诞丽,而這個(gè)子類(lèi)有有兩個(gè)子對(duì)象StringAdd和IntAdd,下面來(lái)看下主函數(shù)中定義的sum函數(shù)拐格,它是一個(gè)含有泛型的函數(shù)僧免,它的第一個(gè)參數(shù)是含有泛型的List類(lèi)型的xs,第二個(gè)參數(shù)是含有泛型的SubTemplate類(lèi)型的隱式參數(shù)m捏浊,函數(shù)返回值是一個(gè)泛型懂衩,首先,函數(shù)里先判斷傳入的第一個(gè)參數(shù)是否為空金踪,若為空則調(diào)用隱式參數(shù)m浊洞,有由于scala可以自動(dòng)進(jìn)行類(lèi)型推到,所以運(yùn)行時(shí)胡岔,泛型T是一個(gè)確定類(lèi)型法希,要么為Int要么為String,但是為空時(shí)靶瘸,我們調(diào)用隱式參數(shù)m的unit是不同的苫亦,Int為0尖淘,而String為“”,所以我們定義了兩個(gè)隱式對(duì)象對(duì)其進(jìn)行處理著觉,IntAdd隱式類(lèi)復(fù)寫(xiě)了unit村生,使之為0,StringAdd隱式類(lèi)復(fù)寫(xiě)了unit饼丘,使之為””,這樣程序就可以正常執(zhí)行了趁桃。同理,隱式對(duì)象方法對(duì)add方法進(jìn)行復(fù)寫(xiě)肄鸽,完成了sum操作卫病。

  • 隱式類(lèi)
    在scala2.10后提供了隱式類(lèi),可以使用implicit聲明類(lèi)典徘,但是需要注意以下幾點(diǎn):
    1.其所帶的構(gòu)造參數(shù)有且只能有一個(gè)
    2.隱式類(lèi)必須被定義在類(lèi)蟀苛,伴生對(duì)象和包對(duì)象里
    3.隱式類(lèi)不能是case class(case class在定義會(huì)自動(dòng)生成伴生對(duì)象與2矛盾)
    4.作用域內(nèi)不能有與之相同名稱(chēng)的標(biāo)示符
import scala.io.Source
import java.io.File

object Implicit_Class {
  import Context_Helper._
  def main(args: Array[String]) {
    println(1.add(2))  
    println(new File("I:\\aa.txt").read())
  }
}

object Context_Helper{
  implicit class ImpInt(tmp:Int){
    def add(tmp2: Int) = tmp + tmp2
  }
  implicit class FileEnhance(file:File){
    def read() = Source.fromFile(file.getPath).mkString
  }
}

其中1.add(2)這個(gè)可以這樣理解:

當(dāng) 1.add(2) 時(shí),scala 編譯器不會(huì)立馬報(bào)錯(cuò)逮诲,而檢查當(dāng)前作用域有沒(méi)有 用implicit 修飾的帜平,同時(shí)可以將Int作為參數(shù)的構(gòu)造器,并且具有方法add的類(lèi)梅鹦,經(jīng)過(guò)查找 發(fā)現(xiàn) ImpInt 符合要求裆甩,利用隱式類(lèi) ImpInt 執(zhí)行add 方法。

歸納一下

  • 隱式轉(zhuǎn)換的時(shí)機(jī)
    a.當(dāng)方法中的參數(shù)的類(lèi)型與目標(biāo)類(lèi)型不一致時(shí)
    b.當(dāng)對(duì)象調(diào)用類(lèi)中不存在的方法或成員時(shí)齐唆,編譯器會(huì)自動(dòng)將對(duì)象進(jìn)行隱式轉(zhuǎn)換
  • 隱式解析機(jī)制
    a.首先會(huì)在當(dāng)前代碼作用域下查找隱式實(shí)體(隱式方法 隱式類(lèi) 隱式對(duì)象)
    b.如果第一條規(guī)則查找隱式實(shí)體失敗嗤栓,會(huì)繼續(xù)在隱式參數(shù)的類(lèi)型的作用域里查找
  • 隱式轉(zhuǎn)換的前提
    a.不存在二義性
    b.隱式操作不能嵌套使用
    c.代碼能夠在不使用隱式轉(zhuǎn)換的前提下能編譯通過(guò),就不會(huì)進(jìn)行隱式轉(zhuǎn)換

  1. 老子《道德經(jīng)》第三十七章箍邮,老子故里茉帅,中國(guó)鹿邑。 ?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锭弊,一起剝皮案震驚了整個(gè)濱河市堪澎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌廷蓉,老刑警劉巖全封,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件马昙,死亡現(xiàn)場(chǎng)離奇詭異桃犬,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)行楞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)攒暇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人子房,你說(shuō)我怎么就攤上這事形用【驮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵田度,是天一觀的道長(zhǎng)妒御。 經(jīng)常有香客問(wèn)我,道長(zhǎng)镇饺,這世上最難降的妖魔是什么乎莉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮奸笤,結(jié)果婚禮上惋啃,老公的妹妹穿的比我還像新娘。我一直安慰自己监右,他們只是感情好边灭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著健盒,像睡著了一般绒瘦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扣癣,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天椭坚,我揣著相機(jī)與錄音,去河邊找鬼搏色。 笑死善茎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的频轿。 我是一名探鬼主播垂涯,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼航邢!你這毒婦竟也來(lái)了耕赘?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤膳殷,失蹤者是張志新(化名)和其女友劉穎操骡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體赚窃,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡册招,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了勒极。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片是掰。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辱匿,靈堂內(nèi)的尸體忽然破棺而出键痛,到底是詐尸還是另有隱情炫彩,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布絮短,位于F島的核電站江兢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏丁频。R本人自食惡果不足惜划址,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望限府。 院中可真熱鬧夺颤,春花似錦、人聲如沸胁勺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)署穗。三九已至寥裂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間案疲,已是汗流浹背封恰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留褐啡,地道東北人诺舔。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像备畦,于是被迫代替她去往敵國(guó)和親低飒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容