歡迎轉(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è)從類型S
到T
的轉(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 PersonFormatterscala> 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