Kotlin——高級(jí)篇(一):Lambda表達(dá)式詳解

(轉(zhuǎn)載)原文鏈接:https://www.cnblogs.com/Jetictors/p/8647888.html


image

經(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ǔ)作瞄。

目錄

image

一、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é):

  1. lambda表達(dá)式總是被大括號(hào)括著伸辟。
  2. 定義完整的Lambda表達(dá)式如上面實(shí)例中的語法2,它有其完整的參數(shù)類型標(biāo)注馍刮,與表達(dá)式返回值信夫。當(dāng)我們把一些類型標(biāo)注省略的情況下,就如上面實(shí)例中的語法2的另外一種類型卡啰。當(dāng)它推斷出的返回值類型不為'Unit'時(shí)静稻,它的返回值即為->符號(hào)后面代碼的最后一個(gè)(或只有一個(gè))表達(dá)式的類型。
  3. 在上面例子中語法3的情況表示為:高階函數(shù)匈辱,當(dāng)Lambda表達(dá)式作為其一個(gè)參數(shù)時(shí)振湾,只為其表達(dá)式提供了參數(shù)類型與返回類型,所以亡脸,我們?cè)谡{(diào)用此高階函數(shù)的時(shí)候我們要為該Lambda表達(dá)式寫出它的具體實(shí)現(xiàn)押搪。
  1. 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ū)別:

  1. 匿名函數(shù)的參數(shù)傳值,總是在小括號(hào)內(nèi)部傳遞围苫。而Lambda表達(dá)式傳值裤园,可以有省略小括號(hào)的簡(jiǎn)寫寫法。
  2. 在一個(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)力棉胀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末法瑟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子唁奢,更是在濱河造成了極大的恐慌霎挟,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驮瞧,死亡現(xiàn)場(chǎng)離奇詭異氓扛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)论笔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門采郎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人狂魔,你說我怎么就攤上這事蒜埋。” “怎么了最楷?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵整份,是天一觀的道長(zhǎng)待错。 經(jīng)常有香客問我,道長(zhǎng)烈评,這世上最難降的妖魔是什么火俄? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮讲冠,結(jié)果婚禮上瓜客,老公的妹妹穿的比我還像新娘。我一直安慰自己竿开,他們只是感情好谱仪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著否彩,像睡著了一般疯攒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上列荔,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天敬尺,我揣著相機(jī)與錄音,去河邊找鬼肌毅。 笑死筷转,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悬而。 我是一名探鬼主播呜舒,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼笨奠!你這毒婦竟也來了袭蝗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤般婆,失蹤者是張志新(化名)和其女友劉穎到腥,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蔚袍,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乡范,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啤咽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晋辆。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宇整,靈堂內(nèi)的尸體忽然破棺而出瓶佳,到底是詐尸還是另有隱情,我是刑警寧澤鳞青,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布霸饲,位于F島的核電站为朋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏厚脉。R本人自食惡果不足惜习寸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望器仗。 院中可真熱鬧融涣,春花似錦、人聲如沸精钮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽轨香。三九已至,卻和暖如春幼东,著一層夾襖步出監(jiān)牢的瞬間臂容,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工根蟹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脓杉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓简逮,卻偏偏與公主長(zhǎng)得像球散,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子散庶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容