“道常無(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)換
-
老子《道德經(jīng)》第三十七章箍邮,老子故里茉帅,中國(guó)鹿邑。 ?