kotlin 學(xué)習(xí)教程(五) |函數(shù)和函數(shù)式編程

1. 前言

我們知道兜畸,在程序中,通常情況下苫耸。一個(gè)類會(huì)有自己的方法(函數(shù))以及屬性,這些方法代表了該類的特性或者說(shuō)具有的能力儡陨。今天我們一起來(lái)研究一下 kotlin中的函數(shù)褪子。

2.如何聲明一個(gè)函數(shù)

在 kotlin 中,我們通過(guò)關(guān)鍵字 fun 來(lái)聲明一個(gè)函數(shù)

fun multiply(x:Int,y:Int):Int{
   ...  //代碼塊
    return x*y
}

如上面的代碼所示:我們定義了函數(shù)multiply()并指定其返回類型為 Int 類型骗村。

3.Lambda表達(dá)式

3.1 Lambda表達(dá)式介紹

從Java8 開(kāi)始嫌褪,Lambda表達(dá)式在 Lambda表達(dá)式,在其他的編程語(yǔ)言中(例如:Scala語(yǔ)言),這種表達(dá)式是語(yǔ)法糖中的一種,在 kotlin 中胚股,也支持這種語(yǔ)法,它允許把函數(shù)作為一個(gè)方法的參數(shù)笼痛,可以使代碼變的更加簡(jiǎn)潔緊湊。

Lambda表達(dá)式的本質(zhì)其實(shí)是匿名函數(shù)琅拌,因?yàn)樵谄涞讓訉?shí)現(xiàn)中還是通過(guò)匿名函數(shù)來(lái)實(shí)現(xiàn)的缨伊。但是我們?cè)谟玫臅r(shí)候不必關(guān)心起底層實(shí)現(xiàn)。不過(guò)Lambda的出現(xiàn)確實(shí)是減少了代碼量的編寫(xiě)进宝,同時(shí)也是代碼變得更加簡(jiǎn)潔明了刻坊。

3.2 Java 8 Lambda 表達(dá)式
3.2.1 語(yǔ)法:
(parameters) -> expression
或
(parameters) ->{ statements; }
3.2.2 特征
  • 可選類型聲明:不需要聲明參數(shù)類型,編譯器可以統(tǒng)一識(shí)別參數(shù)值党晋。

  • 可選的參數(shù)圓括號(hào):一個(gè)參數(shù)無(wú)需定義圓括號(hào)谭胚,但多個(gè)參數(shù)需要定義圓括號(hào)徐块。

  • 可選的大括號(hào):如果主體包含了一個(gè)語(yǔ)句,就不需要使用大括號(hào)灾而。

  • 可選的返回關(guān)鍵字:如果主體只有一個(gè)表達(dá)式返回值則編譯器會(huì)自動(dòng)返回值胡控,大括號(hào)需要指定明表達(dá)式返回了一個(gè)數(shù)值。

3.2.3 代碼如下:
// 1. 不需要參數(shù),返回值為 5  
() -> 5  
  
// 2. 接收一個(gè)參數(shù)(數(shù)字類型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2個(gè)參數(shù)(數(shù)字),并返回他們的差值  
(x, y) -> x – y  
  
// 4. 接收2個(gè)int型整數(shù),返回他們的和  
(int x, int y) -> x + y  
  
// 5. 接受一個(gè) string 對(duì)象,并在控制臺(tái)打印,不返回任何值(看起來(lái)像是返回void)  
(String s) -> System.out.print(s)

從上面可以看出绰疤,代碼簡(jiǎn)潔了不少铜犬。

我們?cè)賮?lái)看一個(gè)例子,在java中假如要計(jì)算兩個(gè)數(shù)的和轻庆,則代碼如下:

import java.util.function.BiFunction;

public class helloword {
    public static void main(String[] args) {
        BiFunction<Integer,Integer,Integer> sum=(Integer x, Integer y) ->{
            return x+y;
        };
        System.out.println("sum(3, 4) = " + sum.apply(3, 4));
    }

}

運(yùn)行結(jié)果如下:


Snipaste_2020-02-17_10-31-55.png

但是癣猾,在 java 8 以下版本,并不支持 Lambda 表達(dá)式余爆,而這個(gè)時(shí)候.kotlin 完美的兼容了 java 8 以下版本對(duì) lamdba 表達(dá)式的支持纷宇,并且能夠進(jìn)行混合開(kāi)發(fā)。

3.3 kotlin Lamdba 表達(dá)式

好蛾方,我們?cè)賮?lái)看看 Android 中 kotlin 的使用,Kotlin語(yǔ)言中

3.3.1 Lamdab 表達(dá)式 的聲明:

lambda表達(dá)式的完整語(yǔ)法如下:

{ params -> expressions }
  • params表示參數(shù)列表像捶,expressions表示具體實(shí)現(xiàn),可以是單行語(yǔ)句桩砰,也可以是多行語(yǔ)句拓春。

  • Lamdba 表達(dá)式的值為大括號(hào)最后一行的值。

3.3,2 Lamdba 表達(dá)式的類型表示:
  • () ->unit 無(wú)參亚隅,返回值為Unit

  • (Int)-> Int 傳入整型硼莽,返回一個(gè)整型

  • (String,(String)->String)->Boolean 傳入字符串,Lamdba 表達(dá)式煮纵,返回 Boolean

3.3.3 Lamdba 表達(dá)式的調(diào)用:
  • 使用 () 進(jìn)行調(diào)用懂鸵,相當(dāng)于invoke()
3.3.4 Lamdba 表達(dá)式的簡(jiǎn)化:
args.forEach(){ println(it) }  // 如果函數(shù)的最后一個(gè)參數(shù)是lamdba 表達(dá)式,則可以將lamdba 放在括號(hào)外面
args.forEach{ println(it) }  // 如果函數(shù)參數(shù)只有一個(gè)lamdba表達(dá)式,則調(diào)用時(shí)小括號(hào)可以省略行疏。
args.forEach(::print)    //入?yún)⒋夜猓祷刂蹬c形參一致的函數(shù)可以用函數(shù)引用的方式作為實(shí)參傳入。

上述同樣的功能酿联,kotlin 語(yǔ)言的實(shí)現(xiàn)如下:

  • 自定義函數(shù)來(lái)實(shí)現(xiàn):
fun main(args: Array<String>) {
  val result=sum(3,4)
    println(result)
}
  fun sum(arg1:Int,arg2:Int)=arg1+arg2
  • 使用 Lamdba 表達(dá)式實(shí)現(xiàn):
fun main(args: Array<String>) {
    println(result(3, 4))
}

val result = { arg1: Int, arg2: Int ->
    println("$arg1+$arg2=${arg1 + arg2}")
    arg1 + arg2
}

我們?cè)賮?lái)看個(gè)例子终息,要求:遍歷kotlin 中main函數(shù)的參數(shù),

  • 示例代碼:Android 中最常見(jiàn)的點(diǎn)擊事件
tv_toLogin.setOnClickListener(object:View.OnClickListener{
            override fun onClick(v: View?) {
                Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show() 
            }

        })
  • 使用 Lambda 表達(dá)式的點(diǎn)擊事件
tv_toLogin.setOnClickListener({
            Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show() 
        })

怎么樣贞让?有沒(méi)有感覺(jué) lamdba 表達(dá)式使用起來(lái)既簡(jiǎn)潔又優(yōu)雅采幌。

4. 高階函數(shù)

4.1 什么是高階函數(shù)

高階函數(shù)就是以另一個(gè)函數(shù)作為參數(shù)或返回值的函數(shù),Kotlin 可以以 lambda 或參數(shù)引用作為參數(shù)或返回值震桶,所以,任何以 lambda 或函數(shù)引用作為參數(shù)或返回值的都是高階函數(shù)征绎。

4.2 常見(jiàn)的高階函數(shù)
4.2.1 forEach()函數(shù)
fun main(args: Array<String>) {
   val list= listOf(1,2,3,4,5,6,7,8)
    val newList=ArrayList<Int>()
    list.forEach{
        val newElement=it *2+3
        newList.add(newElement)
    }
    newList.forEach(::println)
}

運(yùn)行結(jié)果如下:

Snipaste_2020-02-17_10-31-55.png
4.2.2 :map()函數(shù)

map: 接受一個(gè)lambda表達(dá)式蹲姐,并且有返回值磨取,形成一個(gè)新的list,實(shí)現(xiàn)對(duì)集合中的元素進(jìn)行修改

好,我們通過(guò)高階函數(shù) map 來(lái)實(shí)現(xiàn)如上的效果:

fun main(args: Array<String>) {
   val list= listOf(1,2,3,4,5,6,7,8)
   val newList=list.map {
       it*2+3
   }
    println(newList)
}

運(yùn)行結(jié)果如下:

Snipaste_2020-02-17_10-31-55.png

以上代碼中通過(guò)高階函數(shù) map 實(shí)現(xiàn)了對(duì)集合中每個(gè)元素乘以2再加3的操作柴墩,不用去遍歷集合中每個(gè)元素忙厌,是不是簡(jiǎn)單了許多。

4.2.3 flatMap()函數(shù)

flatMap是map和flat兩個(gè)函數(shù)的“復(fù)合邏輯江咳,可以將集合中的數(shù)據(jù)進(jìn)行合并成一個(gè)集合逢净。

示例代碼如下:

fun main(args: Array<String>) {
  val list= listOf(
      1..20,
      21..30,
      31..100
  )
  
   val flatList=list.flatMap {
       it.map {
           "NO.$it"
       }
   }

    flatList.forEach{
        println(it)
    }
}

運(yùn)行結(jié)果如下: 將集合中數(shù)字從1~100打印輸出,每個(gè)數(shù)字前標(biāo)有“NO.”


Snipaste_2020-02-17_10-31-55.png
4.2.4 filter()函數(shù)

傳入Lambda 表達(dá)式為 true 是歼指,保留該元素爹土;使用filter對(duì)集合進(jìn)行按條件過(guò)濾

fun main(args: Array<String>) {
    val list= listOf(1,2,3,4,5,6,7,8,9)
    val result=list.filter {
        it%2==0
    }
    println(result)
}

運(yùn)行結(jié)果如下:


Snipaste_2020-02-17_10-31-55.png
4.3 kotlin 中的 特殊函數(shù)
4.3.1 run()函數(shù)

該函數(shù)實(shí)際上可以說(shuō)是let和with兩個(gè)函數(shù)的結(jié)合體,run函數(shù)接收一個(gè)lambda函數(shù)為參數(shù)踩身,以閉包形式返回胀茵,返回值為最后一行的值或者指定的return的表達(dá)式。

示例代碼如下:

fun main(args: Array<String>) {
    FunKotlin().myFun()
    run {
        FunKotlin().myFun()
    }
    run {
        println("kotlin")
    }
}

class FunKotlin{

    fun myFun():String{
        println("開(kāi)始執(zhí)行myFun()函數(shù)")
        return "kotlin 中的特殊函數(shù)"
    }
}

運(yùn)行結(jié)果如下:

Snipaste_2020-02-17_10-31-55.png

在上面的代碼中挟阻,我們定義了myFun()函數(shù)并通過(guò)run() 進(jìn)行調(diào)用琼娘,調(diào)用的結(jié)果即為myFun()的結(jié)果。

run() 源碼如下:

/**
 * Calls the specified function [block] and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

如代碼所示:我們傳入block()參數(shù)附鸽,最終返回了block() 的執(zhí)行結(jié)果脱拼。

4.3.2 apply()函數(shù)

源碼如下:

/**
 * Calls the specified function [block] with `this` value as its receiver and returns `this` value.
 */
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

如代碼所示:我們傳入 block() 函數(shù),先是調(diào)用了block()函數(shù)坷备,然后返回當(dāng)前的調(diào)用者對(duì)象this熄浓,也就是說(shuō)先執(zhí)行完block()代碼塊邏輯后,再次返回當(dāng)前的調(diào)用者對(duì)象击你。

示例代碼如下:

fun main(args: Array<String>) {
    testApply()
}

fun testApply(){
    val list= mutableListOf<String>()
    list.add("A")
    list.add("B")
    list.add("C")
    list.add("D")
    list.add("E")
    println("普通寫(xiě)法:list=${list}")

    val applyList=mutableListOf<String>().apply {
        add("A")
        add("B")
        add("C")
        add("D")
        add("E")
        println("使用apply 函數(shù)寫(xiě)法 this=${this}")
    }
}

運(yùn)行結(jié)果如下:


Snipaste_2020-02-17_10-31-55.png

如代碼所示:我們的需求是創(chuàng)建一個(gè)集合并向其中添加元素"A"玉组,“B”,"C",“D”丁侄,“E”,然后打印出該集合惯雳,相比普通寫(xiě)法,使用apply() 函數(shù)顯然簡(jiǎn)潔了許多鸿摇。

4.3.3 let() 函數(shù)

let擴(kuò)展函數(shù)的實(shí)際上是一個(gè)作用域函數(shù)石景,當(dāng)你需要去定義一個(gè)變量在一個(gè)特定的作用域范圍內(nèi),let函數(shù)的是一個(gè)不錯(cuò)的選擇拙吉;let函數(shù)另一個(gè)作用就是可以避免寫(xiě)一些判斷null的操作

源碼如下:

/**
 * Calls the specified function [block] with `this` value as its argument and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

如代碼所示潮孽,我們重點(diǎn)看最后一行return block(this),就是說(shuō)把當(dāng)前調(diào)用對(duì)象作為參數(shù)傳入block()代碼塊中筷黔。

示例代碼如下:我們以Android中在適配器 adapter 中進(jìn)行網(wǎng)絡(luò)圖片的加載往史。

context?.let {         
  Glide.with(it).load(item.envelopePic).crossFade().into(helper.getView<ImageView>(R.id.iv_envelopePic))
                }

在上面的代碼中,it 指代的即是 context佛舱,意為上下文椎例。</pre>

4.3.4 also()函數(shù)

源碼如下:

/**
 * Calls the specified function [block] with `this` value as its argument and returns `this` value.
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

看最后兩行代碼挨决,先是執(zhí)行了block(this),隨后返回this订歪,即當(dāng)前的調(diào)用者對(duì)象脖祈。

示例代碼如下:

fun testAlsoFun() {
    val a = "ABC".also {
        println(it) //輸出:ABC
    }
    println(a) //輸出:ABC
    a.let {
        println(it) //輸出:ABC
    }
}
fun main(args: Array<String>) {
    testAlsoFun()
}

在上面的代碼中,字符串“ABC”調(diào)用了also()刷晋,會(huì)打印出調(diào)用者 “ABC”.

4.3.5 with() 函數(shù)

源碼如下:

/**
 * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

我們看到with()函數(shù)傳入了一個(gè)接收者對(duì)象receiver盖高,然后使用該對(duì) 象receiver去調(diào)用傳入的Lambda代碼塊receiver.block()。

  • 示例代碼在 Android中我們初始化一個(gè)控件并給其賦值眼虱,Java 語(yǔ)言實(shí)現(xiàn)如下:
TextView text=(TextView)findViewById(R.id.tv_text)
text.setText("哈哈哈")
text.setTextSize(23)
  • kotlin 語(yǔ)言實(shí)現(xiàn)如下:
with(tv_text){
text="哈哈哈"
textSize=23
}

在上面的代碼中喻奥,實(shí)現(xiàn)的功能是一樣的,但是顯然 kotlin 語(yǔ)言更加的簡(jiǎn)潔蒙幻。

5.擴(kuò)展函數(shù)與屬性

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末映凳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子邮破,更是在濱河造成了極大的恐慌诈豌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抒和,死亡現(xiàn)場(chǎng)離奇詭異矫渔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)摧莽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門庙洼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人镊辕,你說(shuō)我怎么就攤上這事油够。” “怎么了征懈?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵石咬,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我卖哎,道長(zhǎng)鬼悠,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任亏娜,我火速辦了婚禮焕窝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘维贺。我一直安慰自己它掂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布溯泣。 她就那樣靜靜地躺著群发,像睡著了一般晰韵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上熟妓,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音栏尚,去河邊找鬼起愈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛译仗,可吹牛的內(nèi)容都是我干的抬虽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼纵菌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼阐污!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起咱圆,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤笛辟,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后序苏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體手幢,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年忱详,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了围来。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡匈睁,死狀恐怖监透,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情航唆,我是刑警寧澤胀蛮,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站佛点,受9級(jí)特大地震影響醇滥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜超营,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一鸳玩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧演闭,春花似錦不跟、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)购城。三九已至,卻和暖如春虐译,著一層夾襖步出監(jiān)牢的瞬間瘪板,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工漆诽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侮攀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓厢拭,卻偏偏與公主長(zhǎng)得像兰英,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子供鸠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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