(轉(zhuǎn)載)原文鏈接:https://www.cnblogs.com/Jetictors/p/8647888.html
經(jīng)過前面一系列對(duì)Kotlin
講解,相信大家已經(jīng)能對(duì)Kotlin
有了一個(gè)基本的認(rèn)識(shí)西篓。如果你又Java
語言方面的編程經(jīng)驗(yàn)愈腾,你可能已經(jīng)不滿足前面的基礎(chǔ)語法了。從這篇文章起岂津,就為大家講解Kotlin
語言中的高級(jí)操作虱黄。
Lambda
語法在Java
中已經(jīng)被廣泛的運(yùn)用,我們?cè)陂_發(fā)Android
中幾乎上每一個(gè)項(xiàng)目也會(huì)在項(xiàng)目中接入Lambda
插件吮成,因?yàn)?code>Lambda確實(shí)能簡(jiǎn)少很多的代碼量橱乱。無獨(dú)有偶辜梳,在Kotlin
中也是Lambda
語法的,在這篇文章中就詳細(xì)的為大家講解Lambda
語法的編寫與使用泳叠,同時(shí)會(huì)后面的Kotlin——高級(jí)篇(二):高階函數(shù)詳解與標(biāo)準(zhǔn)的高階函數(shù)使用打下基礎(chǔ)作瞄。
目錄
一、Lambda介紹
在上面已經(jīng)提到了在
Java
中已經(jīng)被廣泛的運(yùn)用危纫,但是也是在Java8
的時(shí)候才支持這種Lambda
表達(dá)式宗挥。在其他的編程語言中(例如:Scala
語言)。而這種表達(dá)式是語法糖中的一種种蝶。值得慶幸的是契耿,Kotlin
一經(jīng)開源成熟就已經(jīng)支持這種語法。
Lambda
表達(dá)式的本質(zhì)其實(shí)是匿名函數(shù)
螃征,因?yàn)樵谄涞讓訉?shí)現(xiàn)中還是通過匿名函數(shù)
來實(shí)現(xiàn)的宵喂。但是我們?cè)谟玫臅r(shí)候不必關(guān)心起底層實(shí)現(xiàn)。不過Lambda
的出現(xiàn)確實(shí)是減少了代碼量的編寫会傲,同時(shí)也是代碼變得更加簡(jiǎn)潔明了锅棕。
Lambda
作為函數(shù)式編程的基礎(chǔ),其語法也是相當(dāng)簡(jiǎn)單的淌山。這里先通過一段簡(jiǎn)單的代碼演示沒讓大家了解Lambda
表達(dá)式的簡(jiǎn)潔之處裸燎。
例:
// 這里舉例一個(gè)Android中最常見的按鈕點(diǎn)擊事件的例子
mBtn.setOnClickListener(object : View.OnClickListener{
override fun onClick(v: View?) {
Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show()
}
})
等價(jià)于
// 調(diào)用
mBtn.setOnClickListener { Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show() }
二、Lambda使用
關(guān)于Lambda
的使用泼疑,我這里從從來哪個(gè)方面講解德绿,一是先介紹Lambda
表達(dá)式的特點(diǎn),而是從Lambda
的語法使用講解退渗。
2.1移稳、Lambda表達(dá)式的特點(diǎn)
古人云:欲取之,先與之会油。
要學(xué)習(xí)Lambda
表達(dá)式語法个粱,必先了解其特點(diǎn)。我在這里先總結(jié)出Lambda
表達(dá)式的一些特征翻翩。在下面講解到Lambda
語法與實(shí)踐時(shí)大家就明白了都许。即:
Lambda
表達(dá)式總是被大括號(hào)括著- 其參數(shù)(如果存在)在
->
之前聲明(參數(shù)類型可以省略)- 函數(shù)體(如果存在)在
->
后面。
2.2嫂冻、Lambda語法
為了讓大家徹底的弄明白Lambda
語法胶征,我這里用三種用法來講解。并且舉例為大家說明
語法如下:
1. 無參數(shù)的情況 :
val/var 變量名 = { 操作的代碼 }
2. 有參數(shù)的情況
val/var 變量名 : (參數(shù)的類型桨仿,參數(shù)類型睛低,...) -> 返回值類型 = {參數(shù)1,參數(shù)2,... -> 操作參數(shù)的代碼 }
可等價(jià)于
// 此種寫法:即表達(dá)式的返回值類型會(huì)根據(jù)操作的代碼自推導(dǎo)出來钱雷。
val/var 變量名 = { 參數(shù)1 : 類型骂铁,參數(shù)2 : 類型, ... -> 操作參數(shù)的代碼 }
3. lambda表達(dá)式作為函數(shù)中的參數(shù)的時(shí)候,這里舉一個(gè)例子:
fun test(a : Int, 參數(shù)名 : (參數(shù)1 : 類型急波,參數(shù)2 : 類型, ... ) -> 表達(dá)式返回類型){
...
}
實(shí)例講解:
-
無參數(shù)的情況
// 源代碼 fun test(){ println("無參數(shù)") } // lambda代碼 val test = { println("無參數(shù)") } // 調(diào)用 test() => 結(jié)果為:無參數(shù)
-
有參數(shù)的情況,這里舉例一個(gè)兩個(gè)參數(shù)的例子从铲,目的只為大家演示
// 源代碼 fun test(a : Int , b : Int) : Int{ return a + b } // lambda val test : (Int , Int) -> Int = {a , b -> a + b} // 或者 val test = {a : Int , b : Int -> a + b} // 調(diào)用 test(3,5) => 結(jié)果為:8
-
lambda表達(dá)式作為函數(shù)中的參數(shù)的時(shí)候
// 源代碼 fun test(a : Int , b : Int) : Int{ return a + b } fun sum(num1 : Int , num2 : Int) : Int{ return num1 + num2 } // 調(diào)用 test(10,sum(3,5)) // 結(jié)果為:18 // lambda fun test(a : Int , b : (num1 : Int , num2 : Int) -> Int) : Int{ return a + b.invoke(3,5) } // 調(diào)用 test(10,{ num1: Int, num2: Int -> num1 + num2 }) // 結(jié)果為:18
最后一個(gè)的實(shí)現(xiàn)可能大家難以理解,但請(qǐng)不要迷茫澄暮,你繼續(xù)看下去名段,在下面的實(shí)踐和高階函數(shù)中會(huì)為大家介紹。
經(jīng)過上面的實(shí)例講解與語法的介紹泣懊,我們對(duì)其作出一個(gè)總結(jié):
lambda
表達(dá)式總是被大括號(hào)括著伸辟。- 定義完整的
Lambda
表達(dá)式如上面實(shí)例中的語法2,它有其完整的參數(shù)類型標(biāo)注馍刮,與表達(dá)式返回值信夫。當(dāng)我們把一些類型標(biāo)注省略的情況下,就如上面實(shí)例中的語法2的另外一種類型卡啰。當(dāng)它推斷出的返回值類型不為'Unit'時(shí)静稻,它的返回值即為->
符號(hào)后面代碼的最后一個(gè)(或只有一個(gè))表達(dá)式的類型。- 在上面例子中語法3的情況表示為:
高階函數(shù)
匈辱,當(dāng)Lambda
表達(dá)式作為其一個(gè)參數(shù)時(shí)振湾,只為其表達(dá)式提供了參數(shù)類型與返回類型,所以亡脸,我們?cè)谡{(diào)用此高階函數(shù)的時(shí)候我們要為該Lambda
表達(dá)式寫出它的具體實(shí)現(xiàn)押搪。
-
invoke()
函數(shù):表示為通過函數(shù)變量
調(diào)用自身,因?yàn)樯厦胬又械淖兞?code>b是一個(gè)匿名函數(shù)浅碾。
3大州、Lambda實(shí)踐
學(xué)會(huì)了上面講解的語法只是,相信您已能大致的編寫且使用lambda
表達(dá)式了垂谢,不過只會(huì)上面簡(jiǎn)單的語法還不足以運(yùn)用于實(shí)際項(xiàng)目中復(fù)雜的情況厦画。下面從幾個(gè)知識(shí)點(diǎn)講解Lambda
實(shí)踐的要點(diǎn)。
3.1埂陆、it
it
并不是Kotlin
中的一個(gè)關(guān)鍵字(保留字)苛白。it
是在當(dāng)一個(gè)高階函數(shù)中Lambda
表達(dá)式的參數(shù)只有一個(gè)的時(shí)候可以使用it
來使用此參數(shù)。it
可表示為單個(gè)參數(shù)的隱式名稱焚虱,是Kotlin
語言約定的。
例1:
val it : Int = 0 // 即it不是`Kotlin`中的關(guān)鍵字懂版【樵裕可用于變量名稱
例2:?jiǎn)蝹€(gè)參數(shù)的隱式名稱
// 這里舉例一個(gè)語言自帶的一個(gè)高階函數(shù)filter,此函數(shù)的作用是過濾掉不滿足條件的值。
val arr = arrayOf(1,3,5,7,9)
// 過濾掉數(shù)組中元素小于2的元素,取其第一個(gè)打印民鼓。這里的it就表示每一個(gè)元素薇芝。
println(arr.filter { it < 5 }.component1())
例2這個(gè)列子只是給大家it
的使用,filter
高階函數(shù)丰嘉,在后面的Kotlin——高級(jí)篇(四):集合(Array夯到、List、Set饮亏、Map)基礎(chǔ)章節(jié)中會(huì)為大家詳細(xì)講解耍贾,這里不多做介紹。下面為我們自己寫一個(gè)高階函數(shù)去講解it
路幸。關(guān)于高階函數(shù)的定義與使用請(qǐng)參見Kotlin——從無到有系列之高級(jí)篇(二):高階函數(shù)詳解這篇文章荐开。
例3:
fun test(num1 : Int, bool : (Int) -> Boolean) : Int{
return if (bool(num1)){ num1 } else 0
}
println(test(10,{it > 5}))
println(test(4,{it > 5}))
輸出結(jié)果為:
10
0
代碼講解:上面的代碼意思是,在高階函數(shù)test
中简肴,其返回值為Int
類型晃听,Lambda
表達(dá)式以num1
位條件。其中如果Lambda
表達(dá)式的值為false
的時(shí)候返回0砰识,反之返回num1
能扒。故而當(dāng)條件為num1 > 5
這個(gè)條件時(shí)域那,當(dāng)調(diào)用test
函數(shù)酒朵,num1 = 10
返回值就是10,num1 = 4
返回值就是0胃夏。
3.2予借、下劃線(_)
在使用
Lambda
表達(dá)式的時(shí)候越平,可以用下劃線(_
)表示未使用的參數(shù),表示不處理這個(gè)參數(shù)灵迫。
同時(shí)在遍歷一個(gè)Map
集合的時(shí)候秦叛,這當(dāng)非常有用。
舉例:
val map = mapOf("key1" to "value1","key2" to "value2","key3" to "value3")
map.forEach{
key , value -> println("$key \t $value")
}
// 不需要key的時(shí)候
map.forEach{
_ , value -> println("$value")
}
輸出結(jié)果:
key1 value1
key2 value2
key3 value3
value1
value2
value3
3.3 匿名函數(shù)
- 匿名函數(shù)的特點(diǎn)是可以明確指定其返回值類型瀑粥。
- 它和常規(guī)函數(shù)的定義幾乎相似挣跋。他們的區(qū)別在于,匿名函數(shù)沒有函數(shù)名狞换。
例:
fun test(x : Int , y : Int) : Int{ fun(x : Int , y : Int) : Int{
常規(guī)函數(shù): return x + y 匿名函數(shù): return x + y
} }
在前面的Kotlin——初級(jí)篇(七):函數(shù)(方法)基礎(chǔ)總結(jié)我們講解過單表達(dá)式函數(shù)避咆。故而,可以簡(jiǎn)寫成下面的方式修噪。
常規(guī)函數(shù) : fun test(x : Int , y : Int) : Int = x + y
匿名函數(shù) : fun(x : Int , y : Int) : Int = x + y
從上面的兩個(gè)例子可以看出查库,匿名函數(shù)與常規(guī)函數(shù)的區(qū)別在于一個(gè)有函數(shù)名,一個(gè)沒有黄琼。
實(shí)例演練:
val test1 = fun(x : Int , y : Int) = x + y // 當(dāng)返回值可以自動(dòng)推斷出來的時(shí)候樊销,可以省略,和函數(shù)一樣
val test2 = fun(x : Int , y : Int) : Int = x + y
val test3 = fun(x : Int , y : Int) : Int{
return x + y
}
println(test1(3,5))
println(test2(4,6))
println(test3(5,7))
輸出結(jié)果為:
8
10
12
從上面的代碼我們可以總結(jié)出匿名函數(shù)
與Lambda
表達(dá)式的幾點(diǎn)區(qū)別:
- 匿名函數(shù)的參數(shù)傳值,總是在小括號(hào)內(nèi)部傳遞围苫。而
Lambda
表達(dá)式傳值裤园,可以有省略小括號(hào)的簡(jiǎn)寫寫法。- 在一個(gè)不帶
標(biāo)簽
的return
語句中剂府,匿名函數(shù)時(shí)返回值是返回自身函數(shù)的值拧揽,而Lambda
表達(dá)式的返回值是將包含它的函數(shù)中返回。
3.4腺占、帶接收者的函數(shù)字面值
在
kotlin
中淤袜,提供了指定的接受者對(duì)象調(diào)用Lambda
表達(dá)式的功能。在函數(shù)字面值的函數(shù)體中湾笛,可以調(diào)用該接收者對(duì)象上的方法而無需任何額外的限定符饮怯。它類似于擴(kuò)展函數(shù)
,它允你在函數(shù)體內(nèi)訪問接收者對(duì)象的成員嚎研。
- 匿名函數(shù)作為接收者類型
匿名函數(shù)語法允許你直接指定函數(shù)字面值的接收者類型蓖墅,如果你需要使用帶接收者的函數(shù)類型聲明一個(gè)變量,并在之后使用它临扮,這將非常有用论矾。
例:
val iop = fun Int.( other : Int) : Int = this + other
println(2.iop(3))
輸出結(jié)果為:
5
-
Lambda表達(dá)式作為接收者類型
要用Lambda表達(dá)式作為接收者類型的前提是接收著類型可以從上下文中推斷出來。
例:這里用官方的一個(gè)例子做說明
class HTML {
fun body() { …… }
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // 創(chuàng)建接收者對(duì)象
html.init() // 將該接收者對(duì)象傳給該 lambda
return html
}
html { // 帶接收者的 lambda 由此開始
body() // 調(diào)用該接收者對(duì)象的一個(gè)方法
}
3.5 閉包
- 所謂
閉包
杆勇,即是函數(shù)中包含函數(shù)贪壳,這里的函數(shù)我們可以包含(Lambda
表達(dá)式,匿名函數(shù)蚜退,局部函數(shù)闰靴,對(duì)象表達(dá)式)。我們熟知钻注,函數(shù)式編程是現(xiàn)在和未來良好的一種編程趨勢(shì)蚂且。故而Kotlin
也有這一個(gè)特性。- 我們熟知幅恋,
Java
是不支持閉包的杏死,Java
是一種面向?qū)ο蟮木幊陶Z言,在Java
中捆交,對(duì)象
是他的一等公民淑翼。函數(shù)
和變量
是二等公民。Kotlin
中支持閉包品追,函數(shù)
和變量
是它的一等公民玄括,而對(duì)象
則是它的二等公民了。
實(shí)例:看一段Java
代碼
public class TestJava{
private void test(){
private void test(){ // 錯(cuò)誤肉瓦,因?yàn)镴ava中不支持函數(shù)包含函數(shù)
}
}
private void test1(){} // 正確惠豺,Java中的函數(shù)只能包含在對(duì)象中+
}
實(shí)例:看一段Kotlin
代碼
fun test1(){
fun test2(){ // 正確银还,因?yàn)镵otlin中可以函數(shù)嵌套函數(shù)
}
}
下面我們講解Kotlin
中幾種閉包的表現(xiàn)形式风宁。
3.5.1洁墙、攜帶狀態(tài)
例:讓函數(shù)返回一個(gè)函數(shù),并攜帶狀態(tài)值
fun test(b : Int): () -> Int{
var a = 3
return fun() : Int{
a++
return a + b
}
}
val t = test(3)
println(t())
println(t())
println(t())
輸出結(jié)果:
7
8
9
3.5.2戒财、引用外部變量热监,并改變外部變量的值
例:
var sum : Int = 0
val arr = arrayOf(1,3,5,7,9)
arr.filter { it < 7 }.forEach { sum += it }
println(sum)
輸出結(jié)果:
9
3.6 在Android
開發(fā)中為RecyclerView
的適配器編寫一個(gè)Item
點(diǎn)擊事件
class TestAdapter(val context : Context , val data: MutableList<String>)
: RecyclerView.Adapter<TestAdapter.TestViewHolder>(){
private var mListener : ((Int , String) -> Unit)? = null
override fun onBindViewHolder(holder: TestViewHolder?, position: Int) {
...
holder?.itemView?.setOnClickListener {
mListener?.invoke(position, data[position])
}
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): TestViewHolder {
return TestViewHolder(View.inflate(context,layoutId,parent))
}
override fun getItemCount(): Int {
return data.size
}
fun setOnItemClickListener(mListener : (position : Int, item : String) -> Unit){
this.mListener = mListener
}
inner class TestViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView)
}
// 調(diào)用
TestAdapter(this,dataList).setOnItemClickListener { position, item ->
Toast.makeText(this,"$position \t $item",Toast.LENGTH_SHORT).show()
}
總結(jié)
Lambda
表達(dá)式是為我們減少了大量的代碼,但是Lambda
表達(dá)式是為后面的高階函數(shù)章節(jié)打下基礎(chǔ)饮寞,雖然在這篇文章中也提到了高階函數(shù)孝扛,但是都是最基礎(chǔ)的,在下一節(jié)中會(huì)為大家介紹自定義高階函數(shù)與Kotlin
自身中常用的高階函數(shù)講解幽崩。
在這一章節(jié)中苦始,講述了Lambda
的語法、使用慌申。以及Lambda
表達(dá)式的一些特性與實(shí)踐操作陌选。當(dāng)然還包含了匿名函數(shù)
這一知識(shí)點(diǎn)。其中最重要的當(dāng)屬Lambda
的實(shí)踐操作蹄溉。如果你看完這篇文章還不甚理解咨油,請(qǐng)?jiān)谧屑?xì)的閱讀一遍并實(shí)際代碼演練,因?yàn)樵诤竺娴母唠A函數(shù)章節(jié)還會(huì)遇到柒爵。
在這最后希望您能給個(gè)關(guān)注役电,因?yàn)槟年P(guān)注,是我繼續(xù)寫文章最好的動(dòng)力棉胀。