观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

版权声明

本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/15630095.html

本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

前言

  此博客将讲解高阶函数的学习使用与实际使用的例子记录.

  了解高阶函数,需要了解一个关键概念: 高阶函数与lambda表达式的关系密不可分. 怎么理解这句话呢? 

   我们可以先可以看看普通函数与高阶函数的区别,普通函数的参数使用实例  高阶函数的参数使用lamdba表达式,如下代码中可以查看

  普通函数: 

       这是一个String类里的普通函数, 这里我们可以看到 函数的参数是一个IntRange类实例, 返回值是一个String

public fun CharSequence.substring(range: IntRange): String = subSequence(range.start, range.endInclusive + 1).toString()

 

  高阶函数:

  这是一个String类里的高阶函数, 这里我们可以看到 函数的参数是一个lamdba表达式(即函数参), 返回值是一个String

public inline fun String.filter(predicate: (Char) -> Boolean): String {
    return filterTo(StringBuilder(), predicate).toString()
}

对比后我们可以这么理解下一个问题, 函数参的优势是什么?

1. 函数参可以表现出很大的灵活性,允许你在里面实现代码逻辑后返回参数

2.符合人类的阅读习惯,易于阅读(前提是你不要嵌套大量的代码与大量的其他高阶函数) . 在设计良好的高阶函数上可以体验到读文本小说的方式进行阅读代码

     

参考 简单理解 Kotlin 中的 inline 关键字

参考 Kotlin inline, noinline and crossinline

内联高阶函数

使用高阶函数为开发带来了便利,但同时也产生了一些性能上的损失,官方是这样描述这个问题:

使用高阶函数会带来一些运行时的效率损失:每一个函数都是一个对象,并且会捕获一个闭包。 即那些在函数体内会访问到的变量。 内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销,但是通过内联化 Lambda 表达式可以消除这类的开销。

为了解决这个问题,可以使用内联函数,用inline修饰的函数就是内联函数,inline修饰符影响函数本身和传给它的 Lambda 表达式,所有这些都将内联到调用处,即编译器会把调用这个函数的地方用这个函数的方法体进行替换,而不是创建一个函数对象并生成一个调用。

高阶函数实践例子

根据传入集合里的时间戳字段,查找一天中最大的时间戳item,并且收集成集合返回

public inline fun <T> Iterable<T>.findTheDailyMaxTimestamp(crossinline predicate: (T) -> Long): List<T> {
    var currentDayTime = 0L
    val timestampArray = this.toMutableList()
    timestampArray.sortByDescending { predicate(it) }
    val returnList = mutableListOf<T>()
    for (item in timestampArray) {
        //这里是关键,从实体类中去得参数里指引的目标的字段
        val time = predicate(item)
        if (currentDayTime == 0L) {
            currentDayTime = time
            returnList.add(item)
            continue
        }
        val currentCalendar = Calendar.getInstance()
        currentCalendar.timeInMillis = currentDayTime
        val crtYear = currentCalendar.get(Calendar.YEAR)
        val crtDay = currentCalendar.get(Calendar.DAY_OF_YEAR)
        val itemCalendar = Calendar.getInstance()
        itemCalendar.timeInMillis = time
        val itemYear = itemCalendar.get(Calendar.YEAR)
        val itemDay = itemCalendar.get(Calendar.DAY_OF_YEAR)
        if (crtYear == itemYear && crtDay == itemDay) {
            continue
        }
        currentDayTime = time
        returnList.add(item)
    }
    return returnList
}

使用了 扩展函数 加 内联函数, 在一些需要查找一天里需要显示日期的数据集合使用,特别适合在下面图片中情景下使用(稍微改造还能变成按月查找)

 

 

使用:

val findList: List<FamilyBean> = mList.findTheDailyMaxTimestamp { it1 -> it1.createTime }

两个集合字段对比的函数

 

多参数判空例子

在kotlin多个参数的判空非常讨厌,需要多层嵌套let函数. 这里我们可以实现一个多参数判空的函数,优化这种代码

/**
 * 参数不等于null就执行
 */
fun <T1, T2> notNullLet(obj1: T1?, obj2: T2?, let: (T1, T2) -> (Unit)) {
    if (obj1 != null && obj2 != null) {
        let(obj1, obj2)
    }
}

/**
 * 参数不等于null就执行
 */
fun <T1, T2, T3> notNullLet(obj1: T1?, obj2: T2?, obj3: T3?, let: (T1, T2, T3) -> (Unit)) {
    if (obj1 != null && obj2 != null && obj3 != null) {
        let(obj1, obj2, obj3)
    }
}

/**
 * 参数不等于null就执行
 */
fun <T1, T2, T3, T4> notNullLet(obj1: T1?, obj2: T2?, obj3: T3?, obj4: T4, let: (T1, T2, T3, T4) -> (Unit)) {
    if (obj1 != null && obj2 != null && obj3 != null && obj4 != null) {
        let(obj1, obj2, obj3, obj4)
    }
}

使用:

        val deviceState = mLockViewModel.deviceStateResult.value

        notNullLet(mLockViewModel.currentBleDeviceInfo?.functionList,
                deviceState,
                mLockViewModel.currentFamilyDevice?.deviceSn) { functionList, deviceState, deviceSn ->
            mCatEyeUserListViewModel.postPasswordData(deviceSn, deviceState, functionList)
            showLoadingDialog()
        }

 

 

 

 

End

posted on 2021-12-01 17:54  观心静  阅读(95)  评论(0编辑  收藏  举报