Kotlin 有趣的扩展函数

2019-06-30  本文已影响0人  小甜李子

Kotlin

是一个目标为Java平台上的新的编程语言。它是简洁、安全、实用和可以跟Java互操作。它能用到所有Java可以用到的地方: 服务端开发,Android应用开发,或者更多。Kotlin可以和所有存在的Java库和框架一起良好工作,有Java一样的效率

在kotlin中,函数和对象一样,都是“一等公民”,这也就表示在kotlin中,函数可以做变量能做的事情,如可以存储在变量与数据结构中、或是作为参数传递给其他高阶函数并且也可以作为高阶函数的返回值、也可以像其他任何非函数值一样调用函数

Kotlin扩展函数

Kotlin支持在不继承或者使用装饰模式的情况下对现有的类进行函数扩展,扩展后我们可以直接通过相应类调用的该扩展函数。函数中可以直接使用this访问到对象的成员变量

定义一个扩展函数

在Android中我们完成一个Toast操作如下,大部分情况我们通过定义utils来方便调用,在用kotlin我们就可以采用扩展函数的形式定义toast操作

// 扩展函数
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()
}
// 调用
toast("hello toast")

在这背后其实,kotlin做的也是将我们定义的扩展函数转换成一个工具类的形式,方法参数传入被扩展对象。只不过在使用上对开发者来说是透明的

标准库中的扩展函数

Kotlin为开发者提供了许多标准扩展函数,下面一起看看let、apply、also、run这四个扩展函数的具体定义以及使用

let扩展函数

方法定义

public inline fun <T, R> T.let(block: (T) -> R): R {
    return block(this)
}

使用例子

        // 对变量进行判空、流程规范
        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扩展函数

方法定义

public inline fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}

使用例子

        // 对象的构建,或者连续调用其多个方法,可省略前缀
        var paint = Paint().apply {
            isAntiAlias = true
            color = Color.WHITE
            xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
        }
also扩展函数

方法定义

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扩展函数

方法定义

public inline fun <T, R> T.run(block: T.() -> R): R {
    return block()
}

使用例子

        file?.run { 
            listFiles()
        }?.run { 
            forEach { 
                it.delete()
            }
        }
关于inline关键字

Kotlin天生支持函数式编程,高阶函数和lambda是其一大特色
使用高阶函数会带来一些运行时间效率的损失:实际上调用一个lambda表达式编译时会生成一个匿名内部类并创建其对象,调用其函数来实现。但是如果使用inline关键字的话,当你调用一个inline function的时候,编译器不会产生一次函数调用,而是会在每次调用时,将inline function中的代码直接嵌入到调用处。

let、apply、also、run总结

从上面的各个例子可以可看到这几个扩展函数非常相似,只是在函数体中对于目标对象的引用方式以及函数返回值这两点的不同

扩展函数 引用目标对象方式 返回值
let it 函数返回值
apply this 对象自身
also it 对象自身
run this 函数返回值

Android使用扩展函数

在开发过程中我们可以通过定义扩展函数提供各种更加高效的编码。Android KTX 是一组 Kotlin 扩展程序,属于 Android Jetpack 系列。它优化了供 Kotlin 使用的 Jetpack 和 Android 平台 API。Android KTX 旨在让您利用 Kotlin 语言功能(例如扩展函数/属性、lambda、命名参数和参数默认值),以更简洁、更愉悦、更惯用的方式使用 Kotlin 进行 Android 开发

下面我们来通过Android KTX中的例子来看看官方为我们定义的扩展函数

Animator动画监听

在不使用扩展函数时,我们监听动画是这样的

    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中的扩展函数

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

果然简洁舒服许多,是怎么实现的呢。我们看看Animator.kt的源码

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()

我们用扩展参数是这样的

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

扩展函数定义如下:

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

自定义扩展函数
// 定义EditText扩展函数,方便监听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)
}

扩展函数中我们指定了参数的默认值,所以在使用时我们可以指定我们需要的函数即可。比如我们只需要监听onTextChanged

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

看着还是麻烦,我们再加个扩展函数

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

用的时候,我们单独调用onChanged方法

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

        }
上一篇下一篇

猜你喜欢

热点阅读