Kotlin:扩展函数和高阶函数的运用

前言

在上一篇文章中我们详细的介绍了Kotlin中三个常用的关键字:inline、noinline、crossinline。本篇文章笔者打算结合前面几篇文章的介绍,来讲解我们在实际开发中的运用。通常扩展函数和高阶函数都会结合着使用,这在很大程度上可以简化代码。下面我们开始本篇文章的学习。

1.简化Activity的跳转

通常我们在编写Activity跳转的时候,都会编写如下示例代码:

// 无参数时
val intent = Intent(context, SecondActivity::class.java)
context.startActivity(intent)

// 有参数时
val intent = Intent(context, SecondActivity::class.java)
intent.putExtra("param1","data1")
intent.putExtra("param2","data2")
context.startActivity(intent)

在我们学习完扩展函数和高阶函数后,我们完全可以将上面的代码简化。在Android Studio中选中当前项目右击,New -> Kotlin Class/File在弹出的选择框中,选择File,我们创建一个ExtendFunction.kt的文件。我们编写如下Activity跳转的代码:

// 无参数
fun <T : Activity> Activity.openActivity(clz: Class<T>) {
    val intent = Intent(this, clz)
    startActivity(intent)
}

// 有参数
inline fun <T : Activity> Activity.openActivity(clz: Class<T>, block: Intent.() -> Unit) {
    val intent = Intent(this, clz)
    intent.block()
    startActivity(intent)
}

我们定义了两个同名的Activity扩展函数openActivity(扩展函数和普通的函数一样,支持方法的重载)来实现两个Activity之间的跳转,并且它们都是泛型函数和顶层函数。这样我们就可以在Activity内部或者在拥有Activity实例的情况下访问该函数。在一个Activity内部我们想跳转到另外一个Activity,我们就可以这样写:

// 无参数
openActivity(SecondActivity::class.java)

// 有参数
openActivity(SecondActivity::class.java) {
    putExtra("param1", "data1")
    putExtra("param2", "data2")
}

这样看上去是不是简洁了许多呢。

2.简化Fragment的事务操作

使用Fragment管理碎片化的页面,在Android开发中是最常见的了。首先我们需要先通过FragmentManager的beginTransaction()方法来获取一个事务的对象,然后再进行add、repleace、remove等操作,最后使用commit来提交事务,如下示例代码:

val transaction = supportFragmentManager.beginTransaction()
transaction.add(R.layout.id, Fragment())
transaction.commit()

分析这段代码我们发现,获取事务对象,和commit的操作我们完全可以通过扩展函数来简化。我们需要给FragmentManager添加一个扩展函数,且需要给这个扩展函数添加一个函数类型的参数,这个函数类型的参数需要定义在事务类中,因为我们希望在调用这个扩展函数的时候,能够拥有事务实例的上下文作用域,这样我们就可以进行一系列的事务操作,如下代码:

inline fun FragmentManager.commit(block: FragmentTransaction.() -> Unit) {
    val transaction = beginTransaction()
    transaction.block()
    transaction.commit()
}

在Activity中我们就可以这么写: 

supportFragmentManager.commit {
    add(R.layout.id, Fragment())
}

事实上这块的内容,官方已经在"androidx.fragment:fragment-ktx:..."库中的FragmentManager.kt的文件中帮我实现了。这里为了更好的理解扩展函数和高阶函数的运用我们自己去分析实现了。

3.很好用的updateLayoutParams

通常我们想要动态的去更新View的宽高和边距等属性时,我们会先去获取View的LayoutParams对象,然后更新LayoutParams中的信息,再重新设置给View。比如我们想要动态的更改一个Button的宽高和左边距,我们就会这么写:

val layoutParams = button.layoutParams as ConstraintLayout.LayoutParams
layoutParams.width = 100
layoutParams.height = 100
layoutParams.leftMargin = 100
button.layoutParams = layoutParams

这里因为包裹Button的外层ViewGroup是ConstraintLayout,所以我这里使用的是ConstraintLayout.LayoutParams。而官方为我们提供了View的扩展函数updateLayoutParams:

public inline fun <reified T : ViewGroup.LayoutParams> View.updateLayoutParams(
    block: T.() -> Unit
) {
    val params = layoutParams as T
    block(params)
    layoutParams = params
}

上述示例代码我们就可以简化为:

button.updateLayoutParams<MarginLayoutParams> {
    width = 100
    height = 100
    leftMargin = 100
}

4.runCatching

高阶函数runCatching也是官方给我们提供的一个很好用的高阶函数,它的具体实现在kotlin包下的Result.kt文件中,具体代码如下:

public inline fun <R> runCatching(block: () -> R): Result<R> {
    return try {
        Result.success(block())
    } catch (e: Throwable) {
        Result.failure(e)
    }
}

这里我们有必要先来解释一下try、catch。在Kotlin中try、catch块也可以作为表达式使用,块中的最后一行代码是这个代码块的返回值。
当我们需要包裹一段可能会抛异常的代码我们就可以这么写:

kotlin.runCatching {

}

而runCatching方法的返回值是Result,关于Result类有如下代码:

public value class Result<out T> internal constructor(internal val value: Any?) : Serializable {
    public val isSuccess: Boolean get() = value !is Failure

    public val isFailure: Boolean get() = value is Failure

    public inline fun getOrNull(): T? =
        when {
            isFailure -> null
            else -> value as T
        }
    
    // ...

    public companion object {
        public inline fun <T> success(value: T): Result<T> = Result(value)
    
        public inline fun <T> failure(exception: Throwable): Result<T> =
            Result(createFailure(exception))
    }

    internal class Failure(
        val exception: Throwable
    ) : Serializable {
        override fun equals(other: Any?): Boolean = other is Failure && exception == other.exception
        override fun hashCode(): Int = exception.hashCode()
        override fun toString(): String = "Failure($exception)"
    }
}

为了方便阅读,这里对Result.kt文件中的注解、方法和注释做了删减。例如我们在解析json字符串的时候,我们就可以这么写:

runCatching {
    Gson().fromJson("{"name":"lisha","age":18}", Student::class.java)
}.onSuccess {
   println("onSuccess: $it")
}.onFailure {
    println("onFailure: ${it.message}")
}

// Student类
data class Student(val name:String, var age:Int)
    
// 输出
onSuccess: Student(name=lisha, age=18)    

总结:

扩展函数、高阶函数、带有接受者的函数类型在实际开发中的运用十分的广泛,这里更注重一种思想。比如说我们定义一个带有接收者的函数类型参数,我们就可以在这个函数类型实例化的Lambda表达式中获取接受者的上下文作用域。标准函数库中的apply、also、with、let函数亦是如此。关于带有上下文作用域的Lambda表达式,我们已经在高阶函数和Lambda表达式中详细的介绍,不熟悉的读者可以先去阅读下这块的内容。

好了到这里关于扩展函数和高阶函数的运用我们就讲解完了。下一篇文章继续讲解Kotlin中的基础知识类与继承。我们下期再见~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值