Scala高級特性
Scala混合了面向?qū)ο蠛秃瘮?shù)式的特性奏路,我們通常將可以作為參數(shù)傳遞到方法中的表達式叫做函數(shù)畴椰。在函數(shù)式編程語言中,函數(shù)是“頭等公民”鸽粉,高階函數(shù)包含:作為值的函數(shù)斜脂、匿名函數(shù)、閉包触机、柯里化等等帚戳。
[if !supportLists]2.2.? [endif]?作為值的函數(shù)
可以像任何其他數(shù)據(jù)類型一樣被傳遞和操作的函數(shù),每當(dāng)你想要給算法傳入具體動作時這個特性就會變得非常有用儡首。
定義函數(shù)時格式:val 變量名=(輸入?yún)?shù)類型和個數(shù))=>函數(shù)實現(xiàn)和返回值類型
“=”表示將函數(shù)賦給一個變量
“=>”左面表示輸入?yún)?shù)名稱片任、類型和個數(shù),右邊表示函數(shù)的實現(xiàn)和返回值類型
[if !supportLists]2.3.? [endif]?匿名函數(shù)
在Scala中蔬胯,你不需要給每一個函數(shù)命名对供,沒有將函數(shù)賦給變量的函數(shù)叫做匿名函數(shù)。
由于Scala可以自動推斷出參數(shù)的類型氛濒,所有可以寫的跟精簡一些
還記得神奇的下劃線嗎产场?這才是終極方式
[if !supportLists]2.4.? [endif]?柯里化
[if !supportLists]2.4.1.??[endif]?什么是柯里化
柯里化(Currying)指的是把原來接受多個參數(shù)的函數(shù)變換成接受一個參數(shù)的函數(shù)過程,并且返回接受余下的參數(shù)且返回結(jié)果為一個新函數(shù)的技術(shù)舞竿。
[if !supportLists]2.4.2.??[endif]?例子
[if !supportLists](1)????[endif]一個普通的非柯里化的函數(shù)定義京景,實現(xiàn)一個加法函數(shù):
scala> def plainOldSum(x:Int,y:Int)=x+y
plainOldSum: (x: Int, y: Int)Int
scala> plainOldSum(1,2)
res0: Int = 3
[if !supportLists](2)???? [endif]使用“柯里化”技術(shù)來定義這個加法函數(shù),原來函數(shù)使用一個參數(shù)列表骗奖,“柯里化”确徙,把函數(shù)定義為多個參數(shù)列表:
scala> def curriedSum(x:Int)(y:Int)=x+y
curriedSum: (x: Int)(y: Int)Int
scala> curriedSum(1)(2) (等價于下面?zhèn)z句)
val f1 = curriedSum(1) _
f1(2)
當(dāng)你調(diào)用curriedSum (1)(2)時,實際上是依次調(diào)用兩個普通函數(shù)(非柯里化函數(shù))重归,
第一次調(diào)用使用一個參數(shù)x米愿,返回一個函數(shù)類型的值,
第二次使用參數(shù)y調(diào)用這個函數(shù)類型的值鼻吮。
[if !supportLists](3)???? [endif]使用下面兩個分開的定義在模擬curriedSum柯里化函數(shù):
首先定義第一個函數(shù):
scala> def first(x:Int)=(y:Int)=>x+y
first: (x: Int)Int => Int
然后我們使用參數(shù)1調(diào)用這個函數(shù)來生成第二個函數(shù):
scala> val second =first(1)
second: Int => Int =
scala> second(2)
res2: Int = 3
[if !supportLists](4)????[endif]使用curriedSum 來定義second
scala>? val onePlus=curriedSum(1)_
onePlus:
? Int => Int =
下劃線“_” 作為第二參數(shù)列表的占位符育苟, 這個定義的返回值為一個函數(shù),當(dāng)調(diào)用時會給調(diào)用的參數(shù)加一椎木。
scala>? onePlus(2)
res3:? Int = 3
調(diào)用生成的函數(shù)违柏,給函數(shù)傳入?yún)?shù)博烂,即可得到我們想要的結(jié)果。
[if !supportLists]2.4.3.??[endif]?總結(jié)
scala柯里化風(fēng)格的使用可以簡化主函數(shù)的復(fù)雜度漱竖,提高主函數(shù)的自閉性禽篱,提高功能上的可擴張性、靈活性馍惹√陕剩可以編寫出更加抽象,功能化和高效的函數(shù)式代碼。
[if !supportLists]2.5.? [endif]閉包
[if !supportLists]2.5.1.??[endif]?什么是閉包
閉包是一個函數(shù)万矾,返回值依賴于聲明在函數(shù)外部的一個或多個變量悼吱。閉包通常來講可以簡單的認為是可以訪問不在當(dāng)前作用域范圍內(nèi)的一個函數(shù)。
[if !supportLists]2.5.2.??[endif]?例子
package cn.itcast.closure
/**
? * scala中的閉包
? * 閉包是一個函數(shù)良狈,返回值依賴于聲明在函數(shù)外部的一個或多個變量后添。
? */
object ClosureDemo {
def main(args: Array[String]): Unit = {
val y=10
//變量y不處于其有效作用域時,函數(shù)還能夠?qū)ψ兞窟M行訪問
val add=(x:Int)=>{
????????? x+y
??????? }
//在add中有兩個變量:x和y。其中的一個x是函數(shù)的形式參數(shù)薪丁,
??? //在add方法被調(diào)用時遇西,x被賦予一個新的值。
??? // 然而严嗜,y不是形式參數(shù)粱檀,而是自由變量
??? println(add(5)) // 結(jié)果15
? }
}
[if !supportLists]3.??[endif]?隱式轉(zhuǎn)換和隱式參數(shù)
[if !supportLists]3.1.? [endif]??隱式轉(zhuǎn)換
Scala提供的隱式轉(zhuǎn)換和隱式參數(shù)功能,是非常有特色的功能阻问。是Java等編程語言所沒有的功能梧税。它可以允許你手動指定,將某種類型的對象轉(zhuǎn)換成其他類型的對象或者是給一個類增加方法称近。通過這些功能第队,可以實現(xiàn)非常強大、特殊的功能刨秆。
Scala的隱式轉(zhuǎn)換凳谦,其實最核心的就是定義隱式轉(zhuǎn)換方法,即implicit conversion function衡未。定義的隱式轉(zhuǎn)換方法尸执,只要在編寫的程序內(nèi)引入,就會被Scala自動使用缓醋。Scala會根據(jù)隱式轉(zhuǎn)換方法的簽名如失,在程序中使用到隱式轉(zhuǎn)換方法接收的參數(shù)類型定義的對象時,會自動將其傳入隱式轉(zhuǎn)換方法送粱,轉(zhuǎn)換為另外一種類型的對象并返回褪贵。這就是“隱式轉(zhuǎn)換”。其中所有的隱式值和隱式方法必須放到object中。
然而使用Scala的隱式轉(zhuǎn)換是有一定的限制的脆丁,總結(jié)如下:
[if !supportLists]?? [endif]implicit關(guān)鍵字只能用來修飾方法世舰、變量(參數(shù))。
[if !supportLists]?? [endif]隱式轉(zhuǎn)換的方法在當(dāng)前范圍內(nèi)才有效槽卫。如果隱式轉(zhuǎn)換不在當(dāng)前范圍內(nèi)定義(比如定義在另一個類中或包含在某個對象中)跟压,那么必須通過import語句將其導(dǎo)。
[if !supportLists]3.2.? [endif]??隱式參數(shù)
所謂的隱式參數(shù)歼培,指的是在函數(shù)或者方法中震蒋,定義一個用implicit修飾的參數(shù),此時Scala會嘗試找到一個指定類型的丐怯,用implicit修飾的參數(shù)喷好,即隱式值,并注入?yún)?shù)读跷。
Scala會在兩個范圍內(nèi)查找:
[if !supportLists]?? [endif]當(dāng)前作用域內(nèi)可見的val或var定義的隱式變量;
[if !supportLists]?? [endif]一種是隱式參數(shù)類型的伴生對象內(nèi)的隱式值禾唁;
[if !supportLists]3.3.? [endif]??隱式轉(zhuǎn)換方法作用域與導(dǎo)入
(1)Scala默認會使用兩種隱式轉(zhuǎn)換效览,一種是源類型或者目標類型的伴生對象內(nèi)的隱式轉(zhuǎn)換方法;一種是當(dāng)前程序作用域內(nèi)的可以用唯一標識符表示的隱式轉(zhuǎn)換方法荡短。
(2)如果隱式轉(zhuǎn)換方法不在上述兩種情況下的話丐枉,那么就必須手動使用import語法引入某個包下的隱式轉(zhuǎn)換方法,比如import
test._掘托。通常建議瘦锹,僅僅在需要進行隱式轉(zhuǎn)換的地方,用import導(dǎo)入隱式轉(zhuǎn)換方法闪盔,這樣可以縮小隱式轉(zhuǎn)換方法的作用域弯院,避免不需要的隱式轉(zhuǎn)換。
[if !supportLists]3.4.? [endif]??隱式轉(zhuǎn)換的時機
(1)當(dāng)對象調(diào)用類中不存在的方法或成員時泪掀,編譯器會自動將對象進行隱式轉(zhuǎn)換
(2)當(dāng)方法中的參數(shù)的類型與目標類型不一致時
[if !supportLists]3.5.? [endif]??隱式轉(zhuǎn)換和隱式參數(shù)案例
[if !supportLists]① [endif]隱式轉(zhuǎn)換案例一(讓File類具備RichFile類中的read方法)
package cn.itcast.implic_demo
import java.io.File
import scala.io.Source
object MyPredef{
? //定義隱式轉(zhuǎn)換方法
implicit def file2RichFile(file: File)=new RichFile(file)
}
class RichFile(val f:File) {
def read()=Source.fromFile(f).mkString
}
object RichFile{
def main(args: Array[String]) {
val f=new File("E://words.txt")
??//使用import導(dǎo)入隱式轉(zhuǎn)換方法
??? import MyPredef._
? ??//通過隱式轉(zhuǎn)換听绳,讓File類具備了RichFile類中的方法
val content=f.read()
??? println(conte
nt)
? }
}
[if !supportLists]② [endif]隱式轉(zhuǎn)換案例二(超人變身)
package cn.itcast.implic_demo
class Man(val name:String)
class SuperMan(val name: String) {
def heat=print("超人打怪獸")
}
object SuperMan{
//隱式轉(zhuǎn)換方法
? implicit def man2SuperMan(man:Man)=new SuperMan(man.name)
def main(args: Array[String]) {
val hero=new Man("hero")
//Man具備了SuperMan的方法
????? hero.heat
? }
}
[if !supportLists]③ [endif]隱式轉(zhuǎn)換案例三(一個類隱式轉(zhuǎn)換成具有相同方法的多個類)
package cn.itcast.implic_democlass A(c:C) {
def readBook(): Unit ={
????? println(
"A說:好書好書...")
??? }
}
class B(c:C){
def readBook(): Unit ={
??? println(
"B說:看不懂...")
? }
def writeBook(): Unit ={
??? println(
"B說:不會寫...")
? }
}
class C
object AB{
//創(chuàng)建一個類的2個類的隱式轉(zhuǎn)換
? implicit def C2A(c:C)=new A(c)
implicit def C2B(c:C)=new B(c)
}
object B{
def main(args: Array[String]) {
//導(dǎo)包
??? //1. import AB._ 會將AB類下的所有隱式轉(zhuǎn)換導(dǎo)進來
??? //2. import AB._C2A 只導(dǎo)入C類到A類的的隱式轉(zhuǎn)換方法
??? //3. import AB._C2B 只導(dǎo)入C類到B類的的隱式轉(zhuǎn)換方法
??? import AB._
val c=new C
//由于A類與B類中都有readBook(),只能導(dǎo)入其中一個异赫,否則調(diào)用共同方法時代碼報錯
??? //c.readBook()
??? //C
類可以執(zhí)行B類中的writeBook()
c.writeBook()
? }
}
[if !supportLineBreakNewLine]
[endif]
[if !supportLists]④ [endif]隱式參數(shù)案例四(員工領(lǐng)取薪水)
package cn.itcast.implic_demo
object Company{
//在object中定義隱式值??? 注意:同一類型的隱式值只允許出現(xiàn)一次椅挣,否則會報錯
? implicit? valaaa="zhangsan"
implicit? valbbb=10000.00
}
class Boss {
//注意參數(shù)匹配的類型?? 它需要的是String類型的隱式值
? def callName()(implicit name:String):String={
??? name+
" is coming !"
}
//定義一個用implicit修飾的參數(shù)
? //注意參數(shù)匹配的類型??? 它需要的是Double類型的隱式值
? def getMoney()(implicit money:Double):String={
" 當(dāng)月薪水:"+money
? }
}
object Boss extends App{
//使用import導(dǎo)入定義好的隱式值,注意:必須先加載否則會報錯
? import Company._
val boss =new Boss
? println(
boss.callName()+boss.getMoney())
}