scala 隱式轉(zhuǎn)換- 寫出你的筆記

歡迎轉(zhuǎn)載 請注明出處

蟻人穿越到蜘蛛俠

scala隱式類型對精簡代碼和提升可讀性上有很大的幫助,一直是解零零碎碎學(xué)習(xí),從沒有系統(tǒng)梳理過殴蹄。從時(shí)間線上看臀蛛,我們理解知識的路線并不是從入門到精通,更多的是是從入門到迷糊\放棄粪滤。
現(xiàn)在記錄使用時(shí)的方方面面缎谷。不難哦派撕,因?yàn)椴簧婕袄碚摵途幾g層面的內(nèi)容。

scala的類型推導(dǎo)系統(tǒng)很強(qiáng)大,引入隱式類型在精簡上簡直是如虎添翼片挂。對我們閱讀者而言,隱式類型提升程序擴(kuò)展性和靈活性同時(shí),也引發(fā)可讀性的另一個(gè)爭論-代碼可讀性問題邦马。 在這里,我們不辯爭議,只述說功能宴卖。

內(nèi)容提要

本文內(nèi)容述說隱式類型的用途,以及使用時(shí)的約束邻悬。
每一種隱式類型從使用方法症昏、完整類型簽名例子使用中的約束進(jìn)行說明父丰。

基本隱式類型解析(編譯器是如何查找到缺失信息解析)規(guī)則如下:
1.首先會在當(dāng)前代碼作用域下查找隱式實(shí)體(隱式方法 隱式類 隱式對象)
2.如果第一條規(guī)則查找隱式實(shí)體失敗肝谭,會繼續(xù)在隱式參數(shù)的類型的作用域里查找

詳細(xì)的解析方式可參考 官方隱式類型推導(dǎo)QA

隱私類型聲明

scala中隱式類型定義關(guān)鍵字 implicit.在隱式類型前使用 implicit即可。以隱式類為例:

object Helpers {
    implicit class IntWithTimes(x: Int) {
        def times[A](f: => A): Unit = { }
    } 
}

上例中 IntWithTimes是一個(gè)隱式類.使用隱式類,導(dǎo)入 Helpers對象,我們調(diào)用times時(shí),不再需要new IntWithTimes .那么,implicit 關(guān)鍵字可作用于哪些類型?又如何使用哪?下面一一說明蛾扇。

隱式類型使用規(guī)則
  • 作用域規(guī)則:不管是隱式值攘烛,隱式對象,隱式類或隱式轉(zhuǎn)換函數(shù)镀首,都必須在當(dāng)前的作用域使用才能起作用.

  • 一次性轉(zhuǎn)換規(guī)則:隱式轉(zhuǎn)換從源類型到目標(biāo)類型只會經(jīng)過一次轉(zhuǎn)換坟漱,不會經(jīng)過多次隱式轉(zhuǎn)換達(dá)到。嵌套轉(zhuǎn)換不行

  • 不存在二義性:即相同的類型轉(zhuǎn)換,不能定義兩種方式更哄。

隱式類型方法列表
隱式類型方法之:隱式函數(shù)轉(zhuǎn)換 (implicit conversion)
  • 隱式函數(shù)轉(zhuǎn)換簽名: implicit def name(s:T) :U = {...}

? 隱式函數(shù)轉(zhuǎn)換是提供一個(gè)從類型ST的轉(zhuǎn)換即 S=>T芋齿。

  • 實(shí)例

    case class Person(id:Int = 1,age:Int = 18,add:String = "earth")
    object ImplicitExample extends LogComponent{
    
      //隱式函數(shù)  參數(shù)傳遞值
     implicit def personToInt(person: Person): Int =   person.age
    
     }
    

    Repl的運(yùn)行結(jié)果:scala> Person() * 1 res1: Int = 18

    Person()* 1運(yùn)行,Person類型無法直接進(jìn)行乘法運(yùn)算.編譯器需要查詢基礎(chǔ)類型(Int/Double/Float)和整型進(jìn)行乘法,在作用域內(nèi)查找到 personToInt方法時(shí),即完成了乘法類型匹配.

  • scala 不鼓勵(lì)使用隱式轉(zhuǎn)換,建議使用后面的隱式參數(shù)替代成翩。

隱式類型方法之:隱式值 implicit val/var
  • 隱式值簽名方式: implict var/val varName: Type = ???

  • 實(shí)例

    implicit val implicitValue : Int = 10 //定義implicitValue 作為隱式變量横缔。隱式值是隱式類型轉(zhuǎn)換中使用較少的一種丙曙。主要供隱式參數(shù)使用。后面會說明隱式參數(shù)。

隱式參數(shù)轉(zhuǎn)換和隱式值使用時(shí)莫杈,要定義內(nèi)類/trait/object等類型內(nèi)荆几。

隱式類型方法之: 隱式類implict class
  • 隱式類簽名: implicit calss name(paraName:Type) {....}

  • 實(shí)例

     implicit class PersonFormatter(person: Person) {
       def toImplicitInt: Int = person.age
     }
    

    Repl中運(yùn)行結(jié)果:

    `scala> implicit class PersonFormatter(person: Person) {
    ? | def toImplicitInt: Int = person.age
    ? | }
    defined class PersonFormatter

    scala> Person().toImplicitInt
    res2: Int = 18`

    上例中對于實(shí)現(xiàn)類型 Person => Int轉(zhuǎn)換,使用時(shí),Person對象調(diào)用方法toImplicitInt即可乙漓,無需new PersonFormatter對象。

  • 使用約束/限制

    • 隱式類只能在trait/類/對象內(nèi)部定義

      object Helpers {
      implicit class RichInt(x: Int) // 正確域携!
      }
      implicit class RichDouble(x: Double) // 錯(cuò)誤簇秒!
      

      即隱式類不能單獨(dú)定義,需要?dú)w屬于其它類型內(nèi)部

    • 樣例類(case class)不能是隱式類

        implicit case class T(person: Int) {} //編譯錯(cuò)誤
      

      樣例類在實(shí)例化時(shí)秀鞭,會自動生成伴生對象趋观。這與第一條的約束有沖突。

    • 隱式類必須攜帶參數(shù)锋边,且非隱式參數(shù)只能包含一個(gè)參數(shù)

      implicit class RichDate(date: java.util.Date) // 正確皱坛!
      implicit class Indexer[T](collecton: Seq[T], index: Int) // 錯(cuò)誤!
      implicit class Indexer[T](collecton: Seq[T])(implicit index: Index) // 正確豆巨!
      implicit class PersonFormatter {} // 無非隱式參數(shù)剩辟,編譯錯(cuò)誤
      
  • 應(yīng)用場景

    隱式類主要用于功能擴(kuò)展。如依賴第三方依賴庫往扔,不修改依賴庫的前提下贩猎,擴(kuò)展現(xiàn)有功能。第三方庫變化萍膛,對擴(kuò)展功能修改也是可控的吭服。

隱式類型方法之:隱式參數(shù) implicit parameters
  • 參數(shù)簽名:隱式參數(shù)是隱式類型應(yīng)用較多的一類。它的類型可以是函數(shù)參數(shù)蝗罗、類艇棕、對象、值等

  • 實(shí)例

    • 函數(shù)使用隱式參數(shù)

        implicit val person = Person()
        def implicitPara(implicit  sec: Person ) = sec.age //參數(shù)是隱式類型
      

      Repl執(zhí)行結(jié)果:

      scala> implicitPara res6: Int = 18

    • 函數(shù)柯理化參數(shù)使用隱式參數(shù)

      implicit val person = Person()
      def implicitPara( one:Int)(implicit  sec: Person ) = one + sec.age
    

    以柯理化函數(shù)為例串塑,定義隱式值person:Person沼琉,函數(shù)implicitPara中sec是隱式值類型,調(diào)用時(shí)桩匪,如果傳入sec編譯器在作用域范圍內(nèi)查詢打瘪,找不到就發(fā)生編譯錯(cuò)誤。Repl運(yùn)行結(jié)果:

    scala> implicitPara(1) //person age默認(rèn)值18 res5: Int = 19

    • 隱式參數(shù)是對象

      這一類在后面涉及隱式對象時(shí)詳細(xì)說明吸祟。

  • 使用約束/限制

    • 全部是隱式參數(shù):函數(shù)沒有柯理化時(shí)瑟慈,implicit關(guān)鍵字作用域參數(shù)列表中所有參數(shù)

      //沒有柯理化函數(shù)
      def implicitPara( one:Int,implicit  sec: Person ) = one + sec.age //錯(cuò)誤 屋匕!
      def implicitPara( implicit one:Int葛碧, sec: Person ) = one + sec.age //one sec 都是隱式參數(shù)
      

      不支持部分參數(shù)使用隱式參數(shù)。

    • 部分隱式參數(shù):函數(shù)柯理化時(shí)过吻,可部分指定隱式參數(shù)

      上例說函數(shù)使用隱式參數(shù)必須全部使用进泼。那么蔗衡,若想部分執(zhí)行隱式參數(shù),只能使用柯理化的函數(shù)乳绕。雖然如此绞惦,函數(shù)參數(shù)也只能最后一個(gè)參數(shù)使用implicit使用,否則編譯器報(bào)錯(cuò)洋措。

      //柯理化函數(shù)
      def implicitPara( one:Int)(sec:Int)(implicit  third: Person ) = one + third.age + sec
      def implicitPara( one:Int)(implicit sec:Int济蝉,third: Person ) = one + third.age + sec //編譯錯(cuò)誤
      

    隱式參數(shù)中,隱式類型impilcit關(guān)鍵字只能出現(xiàn)一次菠发,柯理化函數(shù)也不例外王滤。

隱式類型方法之:隱式對象 implicit object
  • 隱式對象簽名 : implict object objName { body }

  • 實(shí)例

     object Example{
        implicit object InnerObject {
        def printAge(person:Person) :Int = {
          println("enter object ImplicitInnerObject")
          person.age
        }
      }  
     }
    
    

    Repl運(yùn)行結(jié)果:

    import Example.InnerObject._ def fun = {import Example.InnerObject._ ;printAge(Person())}scala> fun enter object ImplicitInnerObject res2: Int = 18

  • 使用約束/限制

    隱式對象不能作為隱式參數(shù)。隱式對象如何使用哪滓鸠?如下代碼:

     //定義trait
     trait ImplicitInnerTrait {
      println("enter trait ImplicitInnerTrait")
      def printAge(person:Person) :Int //抽象方法
    }
     //定義隱式對象
     implicit object ImplicitInnerObject extends ImplicitInnerTrait{
        def printAge(person:Person) :Int = { //重寫方法
          println("enter object ImplicitInnerObject")
          person.age
        }
      }
     //定義函數(shù)雁乡,參數(shù)包含隱式參數(shù)對象
      //def callImplic(person:Person)(implicit o:ImplicitInnerObject) = { //錯(cuò)誤! 
      def callImplic(person:Person)(implicit o:ImplicitInnerTrait) = {
        println("call implicit")
        o.printAge(person)
      }
    
隱式類型方法之:隱式宏 implict macro

隱式宏類型2.10.0版本發(fā)布的實(shí)驗(yàn)功能糜俗,該功能不在本文說明范圍內(nèi)踱稍。感興趣的童鞋,參見官方隱式宏介紹

  • ~~類型約束悠抹,隱式轉(zhuǎn)換 <%<~~

    A<%<B 類似于view bound珠月,表示 A可以當(dāng)作B,即A隱式轉(zhuǎn)換成B也滿足楔敌。從查到的資料看在2.10版本已經(jīng)廢棄桥温。

在泛型中,隱式類型應(yīng)用有稍許差異梁丘,如T<% X協(xié)變除包含T``X的包含關(guān)系外,還支持隱式類型的轉(zhuǎn)換旺韭。

總結(jié)

以上氛谜,著重介紹了隱式類型功能,對各類型的如何聲明/使用区端,以及約束都有描述值漫。理解本文滿足解決普通隱式類型如何使用問題。隨著對隱式類型的使用织盼,我們的理解也會越來越深入杨何。

最后,本文雖然不是一篇隱式類型從入門到精通文章沥邻,至少是一篇深入淺出的問題吧危虱。

坊間傳聞,點(diǎn)贊有人愛▄█?█給跪了

* 參考文檔列表 *

https://docs.scala-lang.org/tour/implicit-parameters.html#inner-main
https://docs.scala-lang.org/zh-cn/overviews/core/implicit-classes.html
https://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唐全,一起剝皮案震驚了整個(gè)濱河市埃跷,隨后出現(xiàn)的幾起案子蕊玷,更是在濱河造成了極大的恐慌,老刑警劉巖弥雹,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垃帅,死亡現(xiàn)場離奇詭異,居然都是意外死亡剪勿,警方通過查閱死者的電腦和手機(jī)贸诚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來厕吉,“玉大人酱固,你說我怎么就攤上這事「昂” “怎么了媒怯?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長髓窜。 經(jīng)常有香客問我扇苞,道長,這世上最難降的妖魔是什么寄纵? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任鳖敷,我火速辦了婚禮,結(jié)果婚禮上程拭,老公的妹妹穿的比我還像新娘定踱。我一直安慰自己,他們只是感情好恃鞋,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布崖媚。 她就那樣靜靜地躺著,像睡著了一般恤浪。 火紅的嫁衣襯著肌膚如雪畅哑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天水由,我揣著相機(jī)與錄音荠呐,去河邊找鬼。 笑死砂客,一個(gè)胖子當(dāng)著我的面吹牛泥张,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鞠值,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼媚创,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了彤恶?” 一聲冷哼從身側(cè)響起筝野,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤晌姚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后歇竟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挥唠,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年焕议,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宝磨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,687評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡盅安,死狀恐怖唤锉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情别瞭,我是刑警寧澤窿祥,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布,位于F島的核電站蝙寨,受9級特大地震影響晒衩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜墙歪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一听系、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧虹菲,春花似錦靠胜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至霎褐,卻和暖如春郑藏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瘩欺。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拌牲,地道東北人俱饿。 一個(gè)月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像塌忽,于是被迫代替她去往敵國和親拍埠。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評論 2 349