Kotlin基础 高阶函数和内联函数
高阶函数
一般函数接收的参数有Lambda表达式就可以称为具有函数式编程风格的API, 如果想要定义自己的函数式API, 就需要借助高阶函数.
定义: 如果一个函数接收另一个函数作为参数, 或者返回值类型是另一个函数, 那么就称该函数为高阶函数.
用途: 高阶函数允许让函数类型的参数来决定函数的执行逻辑.
函数类型: 编程语言中有整型, 布尔型等字段类型, 而Kotlin中又增加了函数类型的概念, 如果将这个函数类型添加到一个函数的声明或者返回值声明, 那么这就是一个高阶函数
函数类型语法规则:
(String, Int) -> Unit
-> 的左边用来声明该函数接收什么参数, 多个参数之间使用逗号隔开, 如果不接收参数那么就是空括号; 右边用来声明该函数的返回值, 没有就是Unit相当于Java中的void.
fun example(func: (String, Int) -> Unit){
func("Hello", 111)
}
在函数的传参中, 可以函数名字传入方法名(), 也可以使用 ::方法名 . :: 这种写法, 是一种函数引用方式的写法, 表示函数作为参数传递到函数中.
// 高阶函数定义
fun example(func: (Int, Int) -> Int): Int {
return func(1, 2);
}
// 高阶函数调用 Lambda表达式
example({ int1, int2 ->
int1 + int2
})
// 简化1: 当参数列表最后一个参数为Lambda表达式, 可以移到括号的后面
example(){ int1, int2 ->
int1 + int2
}
// 简化2: 当参数列表为空时, 可以省略空括号
example{ int1, int2 ->
int1 + int2
}
系统apply的实现. 当连续调用同一个对象的多个方法时, apply可以让代码变得更简洁. 以StringBuilder为例
// 定义一个StringBuild扩展函数
fun StringBuilder.build(block: StringBuilder.() -> Unit){
block
}
// StringBuilder.()表示这个函数类型定义在StringBuilder中, 并且在实现体内会持有上下文对象
// 调用
StringBuilder().build {
append("Hi")
append("Hello")
append("world")
}
内联函数
Kotlin的代码最终还是要编译成Java的字节码, 但Java中没有高阶函数的概念, 但是通过Kotlin的编译器将高阶函数的语法转换成了Java支持的语法.
比方你定义的函数类型接收两个参数. 那么Kolin转换的时候会把这个函数类型转换成一个Function接口, 这个接口有一个待实现的invoke() 接口并把定义的两个参数传入了进去. 那么问题就出现了, Lambda表达式在调用的时候被底层转化成了匿名内部类的实现, 而匿名内部类每次的新建都会造成额外的开销. 所以为了解决这个为题出现了内联函数的概念.
内联函数使用很简单, 只要在定义的高阶函数前面加上inline 关键字修饰即可.
其实就是编译器将内联函数中的代码在编译的时候自动替换到调用的地方.
- 先将在调用处的Lambda表达式中的代码,替换到高阶函数定义的实现体内. 这样高阶函数就变成了普通的函数, 没有了函数类型.
- 再将内联函数中的代码全部替换到调用处
noinline
如果一个函数存在多个函数类型的参数时, 想要某一个函数类型不使用内联函数可以如下:
inline fun methodName(block: ()->Unit, noinline bloack2: () -> Unit){}
内联函数虽然可以解决匿名内部类的问题, 但是由于代码在编译期间的替换, 所以没有真正的参数属性. 非内联的函数类型参数可以自由的传递给其他任何函数, 因为他就是一个真实的参数, 而内联函数类型参数只允许传递给另外一个内联函数, 这就是最大的局限性.
内联函数和非内联函数还有一个区别是, 内联函数所引用的Lambda表达式是可以使用return关键字进行函数返回. 而非内联函数只能进行局部返回.
crossinline
inline fun runRunnable( block: ()->Unit){
Runnable{
block()
}
}
// 报错: Can't inline 'block' here: it may contain non-local returns. Add 'crossinline' modifier to parameter declaration 'block'
报错是因为Runnable对象创建的Lambda表达式会在编译的时候转换成匿名内部类的实现方式, 也就是在匿名内部类中调用了传入的函数类型参数.
内联函数所引用的Lambda表达式允许使用return, 但是由于上述代码在匿名内部类中调用了函数类型参数, 此时是不可能进行外层调用函数返回的, 最多只能对匿名内部类中函数返回. 所以出现了上述错误.
这个时候使用crossinline就能解决这个错误提示
inline fun runRunnable(crossinline block: ()->Unit){...}
crossinline就像一个契约, 他能用于保证在内联函数的Lambda表达式中一定不会使用return关键字, 这样就不存在冲突.
声明了交叉函数后, 就不能在调用runRunnable() 函数时的Lambda表达式中使用return关键字, 但是任然可以使用return@runRunnable写法进行局部返回.