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