欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Kotlin中的高階函數(shù)深入講解

 更新時間:2018年11月12日 11:03:56   作者:ScottSong  
這篇文章主要給大家介紹了關(guān)于Kotlin中高階函數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

在Kotlin中,高階函數(shù)是指將一個函數(shù)作為另一個函數(shù)的參數(shù)或者返回值。如果用f(x)、g(x)用來表示兩個函數(shù),那么高階函數(shù)可以表示為f(g(x))。Kotlin為開發(fā)者提供了豐富的高階函數(shù),比如Standard.kt中的let、with、apply等,_Collectioins.kt中的forEach等。為了能夠自如的使用這些高階函數(shù),我們有必要去了解這些高階函數(shù)的使用方法。

函數(shù)類型

在介紹常見高階函數(shù)的使用之前,有必要先了解函數(shù)類型,這對我們理解高階函數(shù)很有幫助。Kotlin 使用類似 (Int) -> String 的一系列函數(shù)類型來處理函數(shù)的聲明,這些類型具有與函數(shù)簽名相對應(yīng)的特殊表示法,即它們的參數(shù)和返回值:

  • 所有函數(shù)類型都有一個圓括號括起來的參數(shù)類型列表以及一個返回類型:(A, B) -> C 表示接受類型分別為 A 與 B 兩個參數(shù)并返回一個 C類型值的函數(shù)類型。參數(shù)類型列表可以為空,如 () -> A ,返回值為空,如(A, B) -> Unit;
  • 函數(shù)類型可以有一個額外的接收者類型,它在表示法中的點之前指定,如類型 A.(B) -> C 表示可以在 A 的接收者對象上,調(diào)用一個以 B 類型作為參數(shù),并返回一個 C 類型值的函數(shù)。
  • 還有一種比較特殊的函數(shù)類型,掛起函數(shù),它的表示法中有一個 suspend 修飾符 ,例如 suspend () -> Unit 或者 suspend A.(B) -> C 。

常用高階函數(shù)

Kotlin提供了很多高階函數(shù),這里根據(jù)這些高階函數(shù)所在文件的位置,分別進(jìn)行介紹,先來看一下常用的高階函數(shù),這些高階函數(shù)在Standard.kt文件中。

1.TODO

先來看一下TODO的源碼:

/**
 * Always throws [NotImplementedError] stating that operation is not implemented.
 */

@kotlin.internal.InlineOnly
public inline fun TODO(): Nothing = throw NotImplementedError()

/**
 * Always throws [NotImplementedError] stating that operation is not implemented.
 *
 * @param reason a string explaining why the implementation is missing.
 */
@kotlin.internal.InlineOnly
public inline fun TODO(reason: String): Nothing = throw NotImplementedError("An operation is not implemented: $reason")

TODO函數(shù)有兩個重載函數(shù),都會拋出一個NotImplementedError的異常。在Java中,有時會為了保持業(yè)務(wù)邏輯的連貫性,對未實現(xiàn)的邏輯添加TODO標(biāo)識,這些標(biāo)識不進(jìn)行處理,也不會導(dǎo)致程序的異常,但是在Kotlin中使用TODO時,就需要針對這些標(biāo)識進(jìn)行處理,否則當(dāng)代碼邏輯運行到這些標(biāo)識處時,就會出現(xiàn)程序的崩潰。

2.run

先給出run函數(shù)的源碼:

/**
 * 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()
}

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

這兩個run函數(shù)都接收一個lambda表達(dá)式,執(zhí)行傳入的lambda表達(dá)式,并且返回lambda表達(dá)式的執(zhí)行結(jié)果。區(qū)別是T.run()是作為泛型T的一個擴(kuò)展函數(shù),所以在傳入的lambda表達(dá)式中可以使用this關(guān)鍵字來訪問這個泛型T中的成員變量和成員方法。

比如,對一個EditText控件,進(jìn)行一些設(shè)置時:

//email 是一個EditText控件
email.run { 
  this.setText("請輸入郵箱地址")
  setTextColor(context.getColor(R.color.abc_btn_colored_text_material))
}

3.with

先看一下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ù)有兩個參數(shù),一個類型為泛型T類型的receiver,和一個lambda表達(dá)式,這個表達(dá)式會作為receiver的擴(kuò)展函數(shù)來執(zhí)行,并且返回lambda表達(dá)式的執(zhí)行結(jié)果。

with函數(shù)與T.run函數(shù)只是寫法上的不同,比如上面的示例可以用with函數(shù):

 with(email, {
  setText("請輸入郵箱地址")
  setTextColor(context.getColor(R.color.abc_btn_colored_text_material))
 })
 
 //可以進(jìn)一步簡化為
 with(email) {
  setText("請輸入郵箱地址")
  setTextColor(context.getColor(R.color.abc_btn_colored_text_material))
 }

4.apply

看一下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
}

apply函數(shù)作為泛型T的擴(kuò)展函數(shù),接收一個lambda表達(dá)式,表達(dá)式的receiver是泛型T,沒有返回值,apply函數(shù)返回泛型T對象本身。可以看到T.run()函數(shù)也是接收lambda表達(dá)式,但是返回值是lambda表達(dá)式的執(zhí)行結(jié)果,這是與apply函數(shù)最大的區(qū)別。

還是上面的示例,可以用apply函數(shù):

 email.apply { 
  setText("請輸入郵箱地址")
 }.apply {
  setTextColor(context.getColor(R.color.abc_btn_colored_text_material))
 }.apply { 
  setOnClickListener { 
  TODO()
  }
 }

5.also

看一下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
}

與apply函數(shù)類似,也是作為泛型T的擴(kuò)展函數(shù),接收一個lambda表達(dá)式,lambda表達(dá)式?jīng)]有返回值。also函數(shù)也返回泛型T對象本身,不同的是also函數(shù)接收的lambda表達(dá)式需要接收一個參數(shù)T,所以在lambda表達(dá)式內(nèi)部,可以使用it,而apply中只能使用this。

關(guān)于this和it的區(qū)別,總結(jié)一下:

  • 如果泛型T,作為lambda表達(dá)式的參數(shù),形如:(T) -> Unit,此時在lambda表示內(nèi)部使用it;
  • 如果泛型T,作為lambda表達(dá)式的接收者,形如:T.() -> Unit,此時在lambda表達(dá)式內(nèi)部使用this;
  • 不論this,還是it,都代表T對象,區(qū)別是it可以使用其它的名稱代替。

還是上面的示例,如果用also函數(shù):

 email.also { 
   it.setText("請輸入郵箱地址")
  }.also { 
   //可以使用其它名稱
   editView -> editView.setTextColor(applicationContext.getColor(R.color.abc_btn_colored_text_material))
  }.also { 
   it.setOnClickListener { 
    //TODO
   }
  }

6.let

看一下let函數(shù)的源碼:

/**
 * 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)
}

let函數(shù)作為泛型T的擴(kuò)展函數(shù),接收一個lambda表達(dá)式,lambda表達(dá)式需要接收一個參數(shù)T,存在返回值。lambda表達(dá)式的返回值就是let函數(shù)的返回值。由于lambda表達(dá)式接受參數(shù)T,所以也可以在其內(nèi)部使用it。

let應(yīng)用最多的場景是用來判空,如果上面示例中的EditText是自定義的可空View,那么使用let就非常方便:

 var email: EditText? = null
  TODO()
  email?.let { 
   email.setText("請輸入郵箱地址")
   email.setTextColor(getColor(R.color.abc_btn_colored_text_material))
  }

7.takeIf

看一下takeIf函數(shù)的源碼:

/**
 * Returns `this` value if it satisfies the given [predicate] or `null`, if it doesn't.
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
 contract {
  callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
 }
 return if (predicate(this)) this else null
}

takeIf函數(shù)作為泛型T的擴(kuò)展函數(shù),接受一個lambda表達(dá)式,lambda表達(dá)式接收一個參數(shù)T,返回Boolean類型,takeIf函數(shù)根據(jù)接收的lambda表達(dá)式的返回值,決定函數(shù)的返回值,如果lambda表達(dá)式返回true,函數(shù)返回T對象本身,如果lambda表達(dá)式返回false,函數(shù)返回null。

還是上面的示例,假設(shè)用戶沒有輸入郵箱地址,進(jìn)行信息提示:

 email.takeIf { 
   email.text.isEmpty()
  }?.setText("郵箱地址不能為空")

8.takeUnless

給出takeUnless函數(shù)的源碼:

/**
 * Returns `this` value if it _does not_ satisfy the given [predicate] or `null`, if it does.
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
 contract {
  callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
 }
 return if (!predicate(this)) this else null
}

takeUnless函數(shù)與takeIf函數(shù)類似,唯一的區(qū)別是邏輯相反,takeUnless函數(shù)根據(jù)lambda表達(dá)式的返回值決定函數(shù)的返回值,如果lambda表達(dá)式返回true,函數(shù)返回null,如果lambda表達(dá)式返回false,函數(shù)返回T對象本身。

還是上面的示例,如果用takeUnless實現(xiàn),就需要調(diào)整一下邏輯:

  email.takeUnless {
   email.text.isNotEmpty() //與takeIf的區(qū)別
  }?.setText("郵箱地址不能為空")

9.repeat

給出repeat函數(shù)的源碼:

/**
 * Executes the given function [action] specified number of [times].
 *
 * A zero-based index of current iteration is passed as a parameter to [action].
 */
@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
 contract { callsInPlace(action) }

 for (index in 0 until times) {
  action(index)
 }
}

repeat函數(shù)接收兩個參數(shù),一個Int型參數(shù)times表示重復(fù)次數(shù),一個lambda表達(dá)式,lambda表達(dá)式接收一個Int型參數(shù),無返回值。repeat函數(shù)就是將我們傳入的lambda表達(dá)式執(zhí)行times次。

 repeat(3) {
  println("執(zhí)行第${it + 1}次")
 }
 
 //運行結(jié)果
執(zhí)行第1次
執(zhí)行第2次
執(zhí)行第3次

由于repeat函數(shù)接收的lambda表達(dá)式,需要一個Int型參數(shù),因此在表達(dá)式內(nèi)部使用it,其實it就是for循環(huán)的索引,從0開始。

總結(jié)

最后對這些高階函數(shù)做一下總結(jié),TODO對比Java中的TODO,需要實現(xiàn)業(yè)務(wù)邏輯,不能放任不理,否則會出現(xiàn)異常,導(dǎo)致崩潰。takeIf、takeUnless這一對都是根據(jù)接收lambda表達(dá)式的返回值,決定函數(shù)的最終返回值是對象本身,還是null,區(qū)別是takeIf,如果lambda表達(dá)式返回true,返回對象本身,否則返回null;takeUnless與takeIf的邏輯正好相反,如果lambda表達(dá)式返回true,返回null,否則返回對象本身。repeat函數(shù),見名知意,將接收的lambda表達(dá)式重復(fù)執(zhí)行指定次。

run、with、apply、also、let這幾個函數(shù)區(qū)別不是很明顯,有時候使用其中一個函數(shù)實現(xiàn)的邏輯,完全也可以用另外一個函數(shù)實現(xiàn),具體使用哪一個,根據(jù)個人習(xí)慣。需要注意的是:

  • 對作為擴(kuò)展函數(shù)的高階函數(shù),使用前需要判斷接收的對象是否為空,比如T.run,apply,also,let在使用前需要進(jìn)行空檢查;
  • 對于返回對象本身的函數(shù),比如apply,also可以形成鏈?zhǔn)秸{(diào)用;
  • 對于在函數(shù)內(nèi)部能夠使用it的函數(shù),it可以用意思更加清晰的變量代替,比如T.run,also,let。

對這幾個函數(shù)的區(qū)別做一個對比:

函數(shù)名稱 是否作為擴(kuò)展函數(shù) 是否返回對象本身 在函數(shù)內(nèi)部使用this/ it
run no no -
T.run yes no it
with no no this
apply yes yes this
also yes yes it
let yes no it

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Android WebView的詳解及實例

    Android WebView的詳解及實例

    這篇文章主要介紹了Android WebView的詳解及實例的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • Android 創(chuàng)建/驗證/刪除桌面快捷方式(已測試可用)

    Android 創(chuàng)建/驗證/刪除桌面快捷方式(已測試可用)

    桌面快捷方式的出現(xiàn)方便了用戶操作,在某些程度上提高了用戶體驗,接下來將介紹下Android創(chuàng)建/驗證/刪除桌面快捷方式的實現(xiàn)思路及代碼,感興趣的朋友可以了解下,或許本文可以幫助到你
    2013-02-02
  • Android觀察者模式實例分析

    Android觀察者模式實例分析

    這篇文章主要介紹了Android觀察者模式,實例分析了Android觀察者模式的原理與相關(guān)使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-04-04
  • Android編程布局控件之AbsoluteLayout用法實例分析

    Android編程布局控件之AbsoluteLayout用法實例分析

    這篇文章主要介紹了Android編程布局控件之AbsoluteLayout用法,結(jié)合實例形式簡單分析了Android絕對布局AbsoluteLayout的使用技巧,需要的朋友可以參考下
    2015-12-12
  • Android實現(xiàn)視頻彈幕功能

    Android實現(xiàn)視頻彈幕功能

    這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)視頻彈幕功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • Android使用屬性動畫如何自定義倒計時控件詳解

    Android使用屬性動畫如何自定義倒計時控件詳解

    自Android 3.0版本開始,系統(tǒng)給我們提供了一種全新的動畫模式,屬性動畫(property animation),它的功能非常強(qiáng)大,下面這篇文章主要給大家介紹了關(guān)于Android使用屬性動畫如何自定義倒計時控件的相關(guān)資料,需要的朋友可以參考下
    2018-05-05
  • Android 對話框sweet-alert-dialog

    Android 對話框sweet-alert-dialog

    這篇文章主要介紹了Android 對話框sweet-alert-dialog的相關(guān)資料,需要的朋友可以參考下
    2016-09-09
  • Flutter組件生命周期和App生命周期示例解析

    Flutter組件生命周期和App生命周期示例解析

    這篇文章主要為大家介紹了Flutter組件生命周期和App生命周期示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 解決Android SurfaceView繪制觸摸軌跡閃爍問題的方法

    解決Android SurfaceView繪制觸摸軌跡閃爍問題的方法

    這篇文章主要為大家詳細(xì)介紹了解決Android SurfaceView繪制觸摸軌跡閃爍問題的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-03-03
  • Android仿支付寶、京東的密碼鍵盤和輸入框

    Android仿支付寶、京東的密碼鍵盤和輸入框

    這篇文章主要介紹了利用Android如何模仿支付寶、京東的密碼鍵盤和輸入框,本文是將鍵盤和輸入框分開來寫,可以根據(jù)需求在各個地方使用,同時處理了大量邏輯,方便快速開發(fā)。感興趣的朋友們可以參考借鑒,下面來一起看看吧。
    2016-10-10

最新評論