上篇介绍了kotlin的基本类型,变量定义、函数定义等,接下来来学习如何使用匿名函数、lambda、闭包,这将大大提高我们使用kotlin编写代码的效率,相对于Java繁琐的代码,你会爱上这种简洁
一.匿名函数
Java也有匿名函数,但是kotlin比Java简洁很多
1.函数变量
如果我们要在Java方法中传入一个回调函数,需要定义一个接口,并使用new关键字实例化匿名类实现该方法:
代码语言:javascript复制 public static void main(String[] args) {
//调用方法,并实例化匿名类实现接口
registerConnectCallback(new ConnectCallback() {
@Override
public void connectCallback(boolean isConnect) {
}
});
}
//定义接口
interface ConnectCallback{
void connectCallback(boolean isConnect);
}
//定义方法 入参为接口
private static void registerConnectCallback(ConnectCallback connectCallback){
connectCallback.connectCallback(true);
}
再来看看kotlin是如何实例化匿名函数的:
代码语言:javascript复制fun main() {
var count = "aabbccddaa".count({ letter ->
letter == 'a'
})
print(count)
}
结果:
4
{}内代表了实例化的匿名函数
2.函数类型和隐式返回
kotlin中函数可以直接赋值给变量,类似于c/c 中的函数指针,而Java我们需要定义接口
代码语言:javascript复制fun main() {
// 变量名 :()代表这是个函数类型变量 -> String代表函数的返回值
val funcp: () -> String = {
"我是一个函数类型变量"
}
println(funcp)
println(funcp())
}
结果:
代码语言:javascript复制Function0<java.lang.String>
我是一个函数类型变量
匿名函数在绝大数情况下,不需要return关键字,默认会使用最后一行代码作为返回值
3.匿名函数入参
我们看1.内置函数的时候,肯定会看不懂kotlin的写法:
代码语言:javascript复制{ letter ->
letter == 'a'
}
其实符号->之前的就是代表了入参,不过这种写法是简化过的,完整的写法如下:
代码语言:javascript复制fun main() {
// ()内表示入参参数类型 //->前代表入参,并赋予了变量名
val funcp: (String) -> String = { name: String ->
"我是一个函数类型变量:$name"
}
println(funcp("张三的函数"))
}
4.it关键字
匿名函数只有一个入参时,可以省略参数名,使用it关键字
代码语言:javascript复制fun main() {
// ()内表示入参参数类型
val funcp: (String) -> String = {
"我是一个函数类型变量:$it"
}
println(funcp("张三的函数,这是it变量"))
}
5.类型推断
和变量的类型推断相同,当初始化时就赋值一个匿名函数,并且没有入参,那么变量就不需要指定类型
代码语言:javascript复制fun main() {
val funcp = {
"我是一个匿名函数"
}
println(funcp())
}
6.入参类型推断
如果匿名函数有入参,那么参数名和入参类型必须有,但是参数名后面不需要指定参数类型了
以3.匿名函数入参中为例子:
代码语言:javascript复制fun main() {
// ()内表示入参参数类型 //->前代表入参,并赋予了变量名
val funcp: (String) -> String = { name ->
"我是一个函数类型变量:$name"
}
println(funcp("张三的函数"))
}
用过lambda的都发现,我们匿名函数其实就是lambda表达式的写法,这种写法大大的精简了代码
二.函数作为参数
1.函数变量作为入参
Java中使用的接口作为入参,而kotlin可以直接传递函数变量
代码语言:javascript复制fun main() {
//定义匿名函数变量
val performCalc: (Int, Int) -> String = { a, b ->
val sum = a b
"$sum"
}
//调用其他函数,传入函数变量
printCalcResult(3, 4, performCalc)
}
fun printCalcResult(a: Int, b: Int, funcp: (Int, Int) -> String) {
//调用匿名函数执行a b
println(funcp(a, b))
}
2.函数作为入参
我们也可以直接传递一个匿名函数
代码语言:javascript复制fun main() {
//调用其他函数,传入函数变量
printCalcResult(3, 4, { a, b ->
val sum = a b
"$sum"
})
}
fun printCalcResult(a: Int, b: Int, funcp: (Int, Int) -> String) {
//调用匿名函数执行a b
println(funcp(a, b))
}
如果匿名函数排在入参的最后,或者匿名函数是唯一参数,那么圆括号可以省略
2.1有其他参数的:
代码语言:javascript复制fun main() {
//调用其他函数,传入函数变量
printCalcResult(3, 4) { a, b ->
val sum = a b
"$sum"
}
}
fun printCalcResult(a: Int, b: Int, funcp: (Int, Int) -> String) {
//调用匿名函数执行a b
println(funcp(a, b))
}
2.2只有一个函数参数:
代码语言:javascript复制fun main() {
//调用其他函数,传入函数变量
printCalcResult { a, b ->
val sum = a b
"$sum"
}
}
fun printCalcResult(funcp: (Int, Int) -> String) {
//调用匿名函数执行a b
println(funcp(3, 4))
}
3.函数内联
在JVM上,定义的lambda会以实例化对象存在,虚拟机会为此分配内存,为了解决这种额外的内存开销,kotlin有一种优化机制叫"内联",内联实际上就是在编译时会把代码复制一份替换lambda
使用方法:inline关键字
代码语言:javascript复制inline fun printCalcResult(funcp: (Int, Int) -> String) {
//调用匿名函数执行a b
println(funcp(3, 4))
}
递归的函数不能使用内联,由于会无限复制,编译时会报错
4.具名函数引用
除了匿名函数外,我们还可以将具名函数作为参数传递
代码语言:javascript复制fun main() {
//将具名函数calc作为参数传递
printCalc(3, 4, ::calc)
}
//具名函数calc
fun calc(a: Int, b: Int): Int {
return a b
}
inline fun printCalc(a: Int, b: Int, p: (Int, Int) -> Int) {
//调用具名函数变量p方法
println(p(a, b))
}
5.返回参数为函数
函数也可以作为函数的返回值
代码语言:javascript复制fun main() {
val p = printCalc()
println(p())
}
//返回类型为 () -> Int 的函数
inline fun printCalc(): () -> Int {
//返回一个匿名函数
return {
val a = 3
val b = 4
a b
}
}
三.闭包
在kotlin中匿名函数可以修改和引用在自己作用域外的变量,而Java要做到这一点,只能用final关键字修饰一个引用型变量,匿名函数引用着定义自己的函数中的变量,kotlin中lambda就是闭包
能接受函数或返回函数的函数叫作高级函数,它们广泛运用于函数式编程中
代码语言:javascript复制fun main() {
val p = printCalc()
println(p())
}
//返回类型为 () -> Int 的函数
inline fun printCalc(): () -> Int {
var a = 3
val b = 4
//返回一个匿名函数
return {
//修改作用域外的变量
a
a b
}
}