Kotlin高阶函数

504 阅读3分钟

一个函数接收另一个函数作为参数,

/**
 * 高阶函数
 * 参数存在函数
 */
fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    return operation(num1, num2)
}

fun plus(num1: Int, num2: Int): Int {
    return num1 + num2
}

fun minusCustom(num1: Int, num2: Int): Int {
    return num1 - num2
}
  • 使用
private fun testHighFunction() {
        val num1 = 100
        val num2 = 80
        val result1 = num1AndNum2(num1, num2, ::plus)
        val result2 = num1AndNum2(num1, num2, ::minusCustom)
    }
  • 换一种方式自定义参数函数的实现
private fun testHighFunction() {
        val num1 = 100
        val num2 = 80
        
        // 第一种写法
        val result1 = num1AndNum2(num1, num2, ::plus)
        val result2 = num1AndNum2(num1, num2, ::minusCustom)
        
        // 第二种写法
        val result3 = num1AndNum2(num1, num2) { n1, n2 -> 
            n1 + n2
        }
        val result4 = num1AndNum2(num1, num2) { n1, n2 ->
            n1 - n2
        }
    }

内联函数

  • 因为Kotlin的高阶函数其实是依赖编译器的强大功能将高阶函数的语法转换为了Java代码。其内部其实是通过匿名内部类来实现的。
  • 这样的话,我们每次调用一次高阶函数,就会创建一个新的匿名内部类,当然也会造成性能上的开销
  • 为了解决这个问题,Kotlin提供了内联函数的功能。它可以将使用Lambda表达式所带来的运行时的开销完全消除
/**
 * 高阶函数
 * 参数存在函数
 */
inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    return operation(num1, num2)
}

fun plus(num1: Int, num2: Int): Int {
    return num1 + num2
}

fun minusCustom(num1: Int, num2: Int): Int {
    return num1 - num2
}
  • 这样的话,Kotlin编译器会将内联函数中的代码在编译的时候自动替换到调用它的地方,这样也就不存在运行时的开销了

noinline、crossinline

  • 一个内联函数中有多个Lambda表达式,但是我们只想内敛其中一个表达式,怎么办呢?很简单,我们可以使用noinline关键字就可以了
inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int, noinline operation2: (Int, Int) -> Int): Int {
    return operation(num1, num2)
}
  • 为什么要这么做呢?
  • 因为内联函数的代码会在编译的时候对调用的地方进行代码替换,因此它没有真正的参数属性。
  • 非内联函数的参数类型可以自由地传递给其他任何函数,因为它就是一个真实的参数,而内联函数的参数类型只允许传递给另外一个内联函数,这也是它最大的局限性。

内联函数和非内联函数的最大的区别

  • 内联函数是可以使用return关键字来进行函数返回的
  • 非内联函数则只能进行局部返回
  • 所以将高阶函数声明成内联函数是一种良好的编程习惯

例外情况

  • 这是因为内联函数允许我们所引用的Lambda表达式使用return关键字进行函数返回,但是由于我们是在匿名函数中调用的函数类型参数,此时是不可能进行外层函数调用函数返回的。那么就出现冲突。
  • 所以,这种情况下,我们需要使用 crossinline关键字
  • 使用了crossinline关键字之后,我们在匿名函数内部就不能调用return关键字进行函数返回了,但是我们可以使用return@runRunnable的形式进行函数返回。总体来说就是:除了在return关键字的使用上有所区别之外,crossinline保留了内联函数的其他所有特性!!
inline fun runRunnable(crossinline block: () -> Unit) {
    val runnable = Runnable {
        block()
    }
}