定义高阶函数

 高阶函数和Lambda的关系是密不可分的。

 定义:如果一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,那么该函数就称为高阶函数。

 一个新概念:函数类型。编程语言中有整型、布尔型等字段类型,而Kotlin增加了函数类型。

 如何定义,语法规则如下:

(String, Int) -> Unit

 

 ->左边是用来声明该函数接收什么参数,多个参数用逗号隔开,如果不接受任何参数,写一对空括号即可;

 ->右边是用来声明该函数的返回值是什么类型,如果没有返回值就使用Unit,相当于Java中的void。  

 

 现在将函数类型添加某个函数的参数声明或者返回值声明上,那么这个函数就是高阶函数了。

fun example(func: (String, Int) -> Unit) {
        func("hello",123)
    }

 

 example()函数接收了一个函数类型的参数,它现在是一个高阶函数。调用方法类似于普通函数。

 

 高阶函数的用途

 高阶函数允许让函数类型的参数来决定函数的执行逻辑。即使是同一个高阶函数,只要传入不同的函数类型参数,那么它的执行逻辑和最终的返回结果就可能是完全不同的。

 例子:

 定义一个num1AndNum2()的高阶函数,接收两个整型和一个函数类型的参数。在此函数中对传入的两个整型参数进行某种运算,并返回运算结果,但是具体进行什么运算是由传入的函数类型参数决定的。

 新建HigherOrderFunction.kt文件,编写如下代码:

fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int{
        val result = operation(num1,num2)
        return result
    }

 

 第三个参数是函数类型,它接收两个整型参数并且返回值也是整型的。在num1AndNum2()中我们是看不到具体运算操作的。

 具体的运算操作函数还需要继续定义,在HigherOrderFunction.kt文件中添加代码:

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

 

 之后我们就可以调用这个高阶函数了:

val num1 = 10
        val num2 = 20
        val result1 = num1AndNum2(num1, num2, ::plus)
        println("result1 is $result1")

 

 可以看到传入的第三个参数用了::,这是一种函数引用方法的写法。

 

 以上虽然是实现了高阶函数,但是每次都要定义一个与函数类型参数相匹配的函数,显得过于复杂了。

 所以,Kotlin还支持其他方式调用高阶函数,比如Lambda表达式,匿名函数,成员引用等。其中Lambda表达式最常用

 * Lambda表达式实现高阶函数

 我们就可以不用写对应的plus函数了:

val result1 = num1AndNum2(num1, num2) {
            n1, n2 -> n1 + n2
        }

 

 Lambda表达式同样可以完整地表达一个函数的参数声明和返回值声明(Lambda表达式中的最后一行代码会自动作为返回值)

 

 高阶函数的原理

 Kotlin代码最终都会编译成Java字节码,但Java中并没有高阶函数。这就要归功于Kotlin强大的编译器了。

 Kotlin编译器会将高阶函数的语法转换成Java支持的语法结构。比如以上代码:

public static int num1AndNum2(int num1, int num2, Function operation) {
        int result = (int) operation.invoke(num1, num2);
        return result;
    }
    public static void main() {
        int num1 = 10;
        int num2 = 20;
        int result = num1AndNum2(num1, num2, new Function() {
            @Override
            public Integer invoke(Integer n1, Integer n2) {
                return n1 + n2;
            }
        });
    }

 

 为了提高可读性,这段代码有些许调整。Function接口是Kotlin内置的一种接口,里面有一个待实现的invoke()函数。

 之前的Lambda表达式在里面变成了Function接口的匿名类实现。这就表明,每调用一次Lambda表达式,都会创建一个新的匿名类实例,所以也会造成额外的内存和性能开销。

 为了消除这个问题有了内联函数,在下一节中