摘自第一行Android代码第3版
定义
如果一个函数接收另一个函数作为参数,或者返回值的类型是另一个和函数,那么该函数就称为高阶函数
(String,Int) -> Unit
-> 左边的部分就是用来声明函数接收什么参数,多个参数之间使用逗号隔开,如果不接受任何参数,写一对空括号就可以了。而->右边的部分用于声明该函数的返回值是什么类型,如果没有任何返回值就使用Unit,它大致相当于Java中的void
举个例子
fun example(func : (String , Int) -> Unit) {
func("hello" , 123)
}
来点实战
package com.example.broadcastbestpractice
fun num1AndNum2(num1 : Int , num2 : Int , operation : (Int , Int) -> Int) : Int {
val result = operation(num1 , num2)
return result
}
fun plus(num1 : Int , num2 : Int) : Int {
return num1 + num2
}
fun minus(num1 : Int , num2 : Int) : Int {
return num1 - num2
}
fun main(){
val num1 = 100
val num2 = 80
val result1 = num1AndNum2(num1,num2,::plus)
val result2 = num1AndNum2(num1,num2,::minus)
println("result1 is $result1")
println("result2 is $result2")
}
使用lambda 替换
val result1 = num1AndNum2(num1 , num2) { n1 , n2 -> n1 + n2 }
val result2 = num1AndNum2(num1 , num2) { n1 , n2 -> n1 - n2 }
另一个例子
fun StringBuilder.build(block : StringBuilder.() -> Unit) : StringBuilder {
block()
return this
}
fun main() {
val list = listOf("Apple" , "Banana" , "Orange" , "Pear" , "Grape")
val result = StringBuilder().build {
append("Start eating fruits.\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
}
println(result.toString())
}
内联函数的使用
内联函数的用法非常简单,只需要在定义高阶函数时加上inline关键字的声明即可
inline fun num1AndNum2(num1 : Int , num2 : Int , operation : (Int , Int) -> Int) : Int {
val result = operation(num1 , num2)
return result
}
noline与crossinline
一个高阶函数中如果接收了两个或者更多个函数类型的参数,这时我们给的函数加上inline关键字,那么kotlin编译器会自动将所有引用的Lambda表达式全部进行内联
但是,如果我们只想内联其中的一个Lambda表达式该怎么办呢?这时就可以使用noinline关键字
inline fun inlineTest(block1 : () -> Unit , noinline block2 : () -> Unit) {
}
可以看到,这里使用inline关键字声明了inlineTest()函数,原来block1和block2这两个函数类参数所引用的Lambda表达式都会被内联。但是我们在block2参数的前面又加上了一个noinline关键字,那么现在就只会对block1参数所引起的Lambda表达式进行内联了,这就是noinline关键字的作用
为什么要用noinine取消内联功能
内联的函数类型参数在编译的时候会被进行代码替换,因此它没有真正的参数属性。非内联的函数参数类型可以自由地传递给其他任何函数,因为它就是一个真实的参数,而内联的函数类型参数只允许传递给另一个内联函数,这也是它最大的局限性
另外还有一个重要的区别,那就是内联函数所引用的Lambda表达式中可以使用return关键字来进行函数返回的,而非内联函数只能进行局部返回
package com.example.broadcastbestpractice
fun printString(str : String , block : (String) -> Unit) {
println("printString begin")
block(str)
println("printString end")
}
fun main() {
println("main start")
val str = ""
printString(str) { s ->
println("lambda start")
if (s.isEmpty()) return@printString
println(s)
println("lambda end")
}
}
输出如下
main start
printString begin
lambda start
printString end
Lambda表达式是不允许直接使用return关键字的,这里使用了return@printString的写法,表示进行局部返回,并且不在执行Lambda的剩余表达式
内联函数时的打印输出情况
inline fun printString(str : String , block : (String) -> Unit) {
println("printString start")
block(str)
println("pinttString end")
}
fun main() {
println("main start")
val str = ""
printString(str) { s ->
println("lambda start")
if (s.isEmpty()) return
println(s)
println("lambda end")
}
}
输出如下
main start
printString start
lambda start
现在printString()函数已经变成了内联函数,我们就可以在Lambda表达式中使用return关键字。此时的return代表的是返回外层的调用函数,也就是main()函数
crossinline
将高阶函数直接声明成内联函数是一种良好的编程习惯,事实上,绝大多数高阶函数是可以直接声明成内联函数的,但是也有少部分例外的情况
inline fun runRunnable(block : () -> Unit){
val runnable = Runnable{
block()
}
runnable.run()
}
这段代码在没有加上inline关键字声明的时候绝对是可以工作的,但是在加上inline关键字之后就会提示错误 Can’t inline ‘block’ here: it may contain non-local returns. Add ‘crossinline’ modifier to parameter declaration ‘block’ 借助crossinline 的声明,代码就可以郑航编译通过了
|