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 ->
}