Kotlin 有趣的擴(kuò)展函數(shù)

Kotlin

是一個目標(biāo)為Java平臺上的新的編程語言。它是簡潔悼枢、安全狂男、實(shí)用和可以跟Java互操作综看。它能用到所有Java可以用到的地方: 服務(wù)端開發(fā),Android應(yīng)用開發(fā)岖食,或者更多红碑。Kotlin可以和所有存在的Java庫和框架一起良好工作,有Java一樣的效率

在kotlin中县耽,函數(shù)和對象一樣句喷,都是“一等公民”镣典,這也就表示在kotlin中,函數(shù)可以做變量能做的事情唾琼,如可以存儲在變量與數(shù)據(jù)結(jié)構(gòu)中兄春、或是作為參數(shù)傳遞給其他高階函數(shù)并且也可以作為高階函數(shù)的返回值、也可以像其他任何非函數(shù)值一樣調(diào)用函數(shù)

Kotlin擴(kuò)展函數(shù)

Kotlin支持在不繼承或者使用裝飾模式的情況下對現(xiàn)有的類進(jìn)行函數(shù)擴(kuò)展锡溯,擴(kuò)展后我們可以直接通過相應(yīng)類調(diào)用的該擴(kuò)展函數(shù)赶舆。函數(shù)中可以直接使用this訪問到對象的成員變量

定義一個擴(kuò)展函數(shù)

在Android中我們完成一個Toast操作如下,大部分情況我們通過定義utils來方便調(diào)用祭饭,在用kotlin我們就可以采用擴(kuò)展函數(shù)的形式定義toast操作

// 擴(kuò)展函數(shù)
inline fun Context.toast(msg: String) {
    Toast.makeText(this, msg, Toast.LENGTH_LONG).show()
}
inline fun Fragment.toast(msg: String) {
    Toast.makeText(activity, msg, Toast.LENGTH_LONG).show()
}
// 調(diào)用
toast("hello toast")

在這背后其實(shí)芜茵,kotlin做的也是將我們定義的擴(kuò)展函數(shù)轉(zhuǎn)換成一個工具類的形式,方法參數(shù)傳入被擴(kuò)展對象倡蝙。只不過在使用上對開發(fā)者來說是透明的

標(biāo)準(zhǔn)庫中的擴(kuò)展函數(shù)

Kotlin為開發(fā)者提供了許多標(biāo)準(zhǔn)擴(kuò)展函數(shù)九串,下面一起看看let、apply寺鸥、also猪钮、run這四個擴(kuò)展函數(shù)的具體定義以及使用

let擴(kuò)展函數(shù)

方法定義

  • T泛型:目標(biāo)擴(kuò)展對象
  • R泛型:函數(shù)參數(shù)的返回值
  • block函數(shù):接口參數(shù)為T對象,返回值為R
  • 執(zhí)行block函數(shù)并返回其結(jié)果
public inline fun <T, R> T.let(block: (T) -> R): R {
    return block(this)
}

使用例子

        // 對變量進(jìn)行判空胆建、流程規(guī)范
        listView?.let {
            it.setFooterDividersEnabled(true)
            it.setSelectionAfterHeaderView()
            it.divider = null
            var adapter = ArrayAdapter<String>(it.context, android.R.layout.simple_list_item_1)
            it.adapter = adapter
            adapter
        }?.let { 
            it.addAll(listOf("item1", "item1"))
        }
apply擴(kuò)展函數(shù)

方法定義

  • T泛型:目標(biāo)擴(kuò)展對象
  • block函數(shù):仍然為T的擴(kuò)展函數(shù)烤低,因此在函數(shù)體內(nèi)可使用this或者直接引用目標(biāo)對象的成員變量
  • 執(zhí)行block函數(shù)并返回目標(biāo)對象自身
public inline fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}

使用例子

        // 對象的構(gòu)建,或者連續(xù)調(diào)用其多個方法笆载,可省略前綴
        var paint = Paint().apply {
            isAntiAlias = true
            color = Color.WHITE
            xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
        }
also擴(kuò)展函數(shù)

方法定義

  • T泛型:目標(biāo)擴(kuò)展對象
  • block函數(shù):接口參數(shù)為T對象扑馁,返回值為空
  • 執(zhí)行block函數(shù)并返回目標(biāo)對象自身
public inline fun <T> T.also(block: (T) -> Unit): T {
    block(this)
    return this
}

使用例子

      stuInfo.also {
            it.append(stu?.name)
            it.append(stu?.age)
            it.append(stu?.code)
        }.toString()
run擴(kuò)展函數(shù)

方法定義

  • T泛型:目標(biāo)擴(kuò)展對象
  • R泛型:函數(shù)參數(shù)的返回值
  • block函數(shù):仍然為T的擴(kuò)展函數(shù),因此在函數(shù)體內(nèi)可使用this或者直接引用目標(biāo)對象的成員變量
  • 執(zhí)行block函數(shù)并返回其結(jié)果
public inline fun <T, R> T.run(block: T.() -> R): R {
    return block()
}

使用例子

        file?.run { 
            listFiles()
        }?.run { 
            forEach { 
                it.delete()
            }
        }
關(guān)于inline關(guān)鍵字

Kotlin天生支持函數(shù)式編程凉驻,高階函數(shù)和lambda是其一大特色
使用高階函數(shù)會帶來一些運(yùn)行時間效率的損失:實(shí)際上調(diào)用一個lambda表達(dá)式編譯時會生成一個匿名內(nèi)部類并創(chuàng)建其對象腻要,調(diào)用其函數(shù)來實(shí)現(xiàn)。但是如果使用inline關(guān)鍵字的話沿侈,當(dāng)你調(diào)用一個inline function的時候闯第,編譯器不會產(chǎn)生一次函數(shù)調(diào)用,而是會在每次調(diào)用時缀拭,將inline function中的代碼直接嵌入到調(diào)用處咳短。

let、apply蛛淋、also咙好、run總結(jié)

從上面的各個例子可以可看到這幾個擴(kuò)展函數(shù)非常相似,只是在函數(shù)體中對于目標(biāo)對象的引用方式以及函數(shù)返回值這兩點(diǎn)的不同

擴(kuò)展函數(shù) 引用目標(biāo)對象方式 返回值
let it 函數(shù)返回值
apply this 對象自身
also it 對象自身
run this 函數(shù)返回值

Android使用擴(kuò)展函數(shù)

在開發(fā)過程中我們可以通過定義擴(kuò)展函數(shù)提供各種更加高效的編碼褐荷。Android KTX 是一組 Kotlin 擴(kuò)展程序勾效,屬于 Android Jetpack 系列。它優(yōu)化了供 Kotlin 使用的 Jetpack 和 Android 平臺 API。Android KTX 旨在讓您利用 Kotlin 語言功能(例如擴(kuò)展函數(shù)/屬性层宫、lambda杨伙、命名參數(shù)和參數(shù)默認(rèn)值),以更簡潔萌腿、更愉悅限匣、更慣用的方式使用 Kotlin 進(jìn)行 Android 開發(fā)

下面我們來通過Android KTX中的例子來看看官方為我們定義的擴(kuò)展函數(shù)

Animator動畫監(jiān)聽

在不使用擴(kuò)展函數(shù)時,我們監(jiān)聽動畫是這樣的

    val animator = ObjectAnimator.ofFloat(view, "translationX", 0f, 100f)
    animator.addListener {
        object : Animator.AnimatorListener {
            override fun onAnimationEnd(animation: Animator?) {
            }

            override fun onAnimationCancel(animation: Animator?) {
            }

            override fun onAnimationStart(animation: Animator?) {
            }

            override fun onAnimationRepeat(animation: Animator?) {
            }

        }
    }

然后我們使用androidx.core.animation.Animator.kt中的擴(kuò)展函數(shù)

val animator = ObjectAnimator.ofFloat(view, "translationX", 0f, 100f)
    animator.addListener(onCancel = {
        // something
    })

果然簡潔舒服許多毁菱,是怎么實(shí)現(xiàn)的呢米死。我們看看Animator.kt的源碼

  • 定義Animator的擴(kuò)展函數(shù)addListener
  • 接收onEnd、onStart贮庞、onCancel峦筒、onRepeat并指定默認(rèn)值為空函數(shù)
  • 擴(kuò)展函數(shù)中創(chuàng)建AnimatorListener的匿名對象并將函數(shù)參數(shù)賦值給對應(yīng)函數(shù)
  • 那么在使用時我們只需要指定我們需要的函數(shù)即可
inline fun Animator.addListener(
    crossinline onEnd: (animator: Animator) -> Unit = {},
    crossinline onStart: (animator: Animator) -> Unit = {},
    crossinline onCancel: (animator: Animator) -> Unit = {},
    crossinline onRepeat: (animator: Animator) -> Unit = {}
): Animator.AnimatorListener {
    val listener = object : Animator.AnimatorListener {
        override fun onAnimationRepeat(animator: Animator) = onRepeat(animator)
        override fun onAnimationEnd(animator: Animator) = onEnd(animator)
        override fun onAnimationCancel(animator: Animator) = onCancel(animator)
        override fun onAnimationStart(animator: Animator) = onStart(animator)
    }
    addListener(listener)
    return listener
}
SharedPreferences

正常情況我們是這樣使用它的

        val sp = getSharedPreferences("test", Context.MODE_PRIVATE)
        sp.edit().putString("name", "liu").commit()
        sp.edit().putInt("age", 20).commit()
        sp.edit().putBoolean("isMale", true).commit()

我們用擴(kuò)展參數(shù)是這樣的

        sp.edit {
            putString("name", "liu")
            putInt("age", 20)
            putBoolean("isMale", true)
        }

擴(kuò)展函數(shù)定義如下:

inline fun SharedPreferences.edit(
    commit: Boolean = false,
    action: SharedPreferences.Editor.() -> Unit
) {
    val editor = edit()
    action(editor)
    if (commit) {
        editor.commit()
    } else {
        editor.apply()
    }
}

  • 定義SharedPreferences 的edit擴(kuò)展函數(shù)
  • commit參數(shù):是否及時commit提交到磁盤文件
  • action參數(shù):是SharedPreferences.Editor的擴(kuò)展函數(shù),需要傳入Editor
自定義擴(kuò)展函數(shù)
// 定義EditText擴(kuò)展函數(shù)窗慎,方便監(jiān)聽TextChange
inline fun EditText.onTextChange(
        crossinline afterChanged: (s: Editable?) -> Unit = {},
        crossinline beforeChanged: (s: CharSequence?, start: Int, count: Int, after: Int) -> Unit = { s, start, couunt, after -> },
        crossinline onChanged: (s: CharSequence?, start: Int, before: Int, count: Int) -> Unit = { s, start, before, count -> }
) {
    val listener = object : TextWatcher {
        override fun afterTextChanged(s: Editable?) = afterChanged(s)
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = beforeChanged(s, start, count, after)
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = onChanged(s, start, before, count)
    }
    addTextChangedListener(listener)
}

擴(kuò)展函數(shù)中我們指定了參數(shù)的默認(rèn)值物喷,所以在使用時我們可以指定我們需要的函數(shù)即可。比如我們只需要監(jiān)聽onTextChanged

        editText.onTextChange(
                onChanged = { c: CharSequence?, i: Int, i1: Int, i2: Int ->
                }
        )

看著還是麻煩捉邢,我們再加個擴(kuò)展函數(shù)

inline fun EditText.onChanged(
        crossinline onChanged: (s: CharSequence?, start: Int, before: Int, count: Int) -> Unit = { _, _, _, _ -> }) {
    onTextChange(onChanged = onChanged)
}

用的時候脯丝,我們單獨(dú)調(diào)用onChanged方法

        editText.onChanged { s, start, before, count ->

        }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市伏伐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晕拆,老刑警劉巖藐翎,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異实幕,居然都是意外死亡吝镣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門昆庇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來末贾,“玉大人,你說我怎么就攤上這事整吆」澳欤” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵表蝙,是天一觀的道長拴测。 經(jīng)常有香客問我,道長府蛇,這世上最難降的妖魔是什么集索? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上务荆,老公的妹妹穿的比我還像新娘妆距。我一直安慰自己,他們只是感情好函匕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布娱据。 她就那樣靜靜地躺著,像睡著了一般浦箱。 火紅的嫁衣襯著肌膚如雪吸耿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天酷窥,我揣著相機(jī)與錄音咽安,去河邊找鬼。 笑死蓬推,一個胖子當(dāng)著我的面吹牛妆棒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沸伏,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼糕珊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了毅糟?” 一聲冷哼從身側(cè)響起红选,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎姆另,沒想到半個月后喇肋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡迹辐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年蝶防,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片明吩。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡间学,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出印荔,到底是詐尸還是另有隱情低葫,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布躏鱼,位于F島的核電站氮采,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏染苛。R本人自食惡果不足惜鹊漠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一主到、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧躯概,春花似錦登钥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至姿锭,卻和暖如春塔鳍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呻此。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工轮纫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人焚鲜。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓掌唾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忿磅。 傳聞我的和親對象是個殘疾皇子糯彬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355