高阶函数是将函数用作参数或返回值的函数。在java中,函数是不可以作为参数或者返回值的,所以要实现这样的功能,会使用接口来中转,比如:
public interface Wrapper {
int method(int param);
}
int a(Wrapper wrapper) {
return wrapper.method(1);
}
a(wrapper1);
a(wrapper2);
在Kotlin中,函数是可以作为参数的
fun a(funParam: (Int) -> String): String {
return funParam(1)
}
可以作为返回值
fun c(param: Int): (Int) -> Unit {
...
}
如果要把函数赋值给一个对象,要使用双":",
val d = ::b
双冒号的写法叫做函数引用 Function Reference,将函数变成对象,「函数可以作为参数」的本质,是函数在 Kotlin 里可以作为对象存在,因为只有对象才能被作为参数传递。在 Kotlin 里,一个函数名的左边加上双冒号,它就不表示这个函数本身了,而表示一个对象,或者说一个指向对象的引用,这个对象不是函数本身,而是一个和这个函数具有相同功能的对象
b(1) // 调用函数
d(1) // 用对象d 后面加上括号来实现 b() 的等价操作,实际上会调用 d.invoke(1)
(::b)(1) // 用对象 :b 后面加上括号来实现 b() 的等价操作,实际上会调用 (::b).invoke(1)
使用:
fun num1AndNum2(num1: Int, num2: Int, method: (a: Int, b: Int) -> Int): Int {
return method(num1, num2)
}
fun plus(num1: Int, num2: Int): Int {
return num1 + num2
}
//三种不同的调用方法
var num1 = 10
var num2 = 20
var result1 = num1AndNum2(num1, num2, ::plus)
println(result1)
var result2 = num1AndNum2(num1, num2, fun(num1: Int, num2: Int): Int {
return num1 - num2
})
println(result2)
//如果 Lambda 是函数的最后一个参数,可以把 Lambda 写在括号的外面,
var result3 = num1AndNum2(num1, num2) { a, b ->
a + b
}
println(result3)
关于lambda的几点说明
如果 Lambda 是函数的最后一个参数,可以把 Lambda 写在括号的外面
view.setOnClickListener() { v: View ->
switchToNextPage()
}
如果 Lambda 是函数唯一的参数,可以直接把括号去掉
view.setOnClickListener { v: View ->
switchToNextPage()
}
如果 Lambda 是单参数的,这个参数可以省略掉不写
view.setOnClickListener {
switchToNextPage()
}
如果用到这个参数,然后没写,可以使用it,Kotlin 的 Lambda 对于省略的唯一参数有默认的名字:it
view.setOnClickListener {
switchToNextPage()
it.setVisibility(GONE)
}
Lambda 的返回值不是用 return 来返回,而是直接取最后一行代码的值
val b: (Int) -> String = {
it.toString() // it 可以被推断出是 Int 类型
}
如果写了return,会把这个作为它外层的函数的返回值来直接结束外层函数。return 从最近的使用 fun 关键字声明的函数返回, lambda 表达式没有使用 fun关键字,所以 lambda 中的 return 从最外层的函数返回
每调用一次 lambda 表达式,一个额外的类就会被创建。如果 lambda 捕捉了某个变量,那么每次调用的时候都会创建一个新的对象 。这就会带来额外的开销。如果使用 inline 修饰符标记一个函数,在函数被使用的时候编译器并不会生成函数调用的代码,而是使用函数实现的真实代码替换每一次的函数调用。这样的函数就是内联函数,用于定义重复调用的函数,减少开销。
使用inline 关键字只能提高带有 lambda 参数的函数的性能,其他的情况需要额外的度量和研究。应该注意代码的长度。如果要内联的函数很大,将它的字节码拷贝到每一个调用点将会极大地增加字节码的长度。
inline fun doCal(a: Int, b: Int, cal: (a: Int, b: Int) -> Int): Int {
return cal(a, b)
}
如果希望只内联一部分传给内联函数的 lambda 表达式参数, 那么可以用 noinline 修饰符标记不希
望内联的函数参数:
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { …… }
参考:Kotlin 的 Lambda 表达式