高阶函数
首先理解一下函数类型这个东西,在kotlin中,函数和Int、String等一样,也是一种类型,也有相应的对象,意味着可以声明变量类型或者作为方法的参数/返回类型。函数类型和扩展函数不一样!扩展函数相当于只是在原有的类中新增了一个方法,但它并不是一种类型。
函数类型的格式(就是把一个普通函数的参数、返回类型提取了出来):
// (基本类型, 基本类型,....) -> 基本类型
// 例如
(Int, String) -> Boolean
声明变量并赋值,这个变量的值可以被改变,但值一定是个函数:
var func: (Int) -> String = { age: Int -> "age = $age" }
作为函数的参数,调用函数类型时直接使用.invoke(),或者直接调用函数:
testFunc(func)
fun testFunc(block: (Int) -> String) {
block.invoke(18)
// block(18) 直接调用也可以
}
作为函数的返回值:
fun testFunc(): (Int) -> String {
return func
}
这样参数或者返回值是个函数类型的函数,例如testFunc()就是一个高阶函数。
有一个混淆的概念也要提一下,那就是匿名函数,格式如下:
// fun(基本类型):基本类型{
// }
// 例如
fun(age:Int):String{
return "age = $age"
}
和普通方法的区别就是没有方法名,但是注意,它是一个函数对象,它不是一个函数!正是因为它是一个函数类型的对象,所以才可以被赋值给变量后者传参。
// 作为参数传给testFunc方法
testFunc(
fun(age:Int):String{
return "age = $age"
}
)
同时也意味着它不能作为顶层函数,也不能作为一个普通函数写在类里面,会报错的。
有时候会看到这样的写法:
testFunc(::func)
fun func(age:Int):String{
return "age = $age"
}
这里的::func
也是一个函数对象,一个类型为func的对象,作为参数传给了testFunc(),也可以赋值给变量或者直接调用:
// 赋值
val f = ::func
// 直接直接调用
(::func).invoke()
f.invoke()
fun func(age:Int):String{
return "age = $age"
}
现在对比一下函数类型和普通类型,就更好理解了
普通类型 | 函数类型 | |
---|---|---|
声明变量 | 例如val s: String | 例如val f: (String)->Unit |
对象赋值 | s = “你好” | f = { name -> } |
作为传参 | fun test(str: String){} test(s) | fun test(block: (String)->Unit){} test(f) |
作为返回值 | fun test():String{ return s } | fun test():(String)->Unit{ return f } |
调用它的方法 | s.length() | f.invoke() |
也就是说函数是函数,函数类型是函数类型,函数对象是函数对象,三者是不一样的。
扩展函数
有时候会遇到这样的写法:
fun testFunc(block: T.()->Unit){}
T.() -> Unit
和普通类型()-> Unit
有什么区别呢,首先他们都是函数类型,不同的地方:()-> Unit
是普通函数,可以在任何地方直接调用invoke,T.()->Unit
是一个带接收者的扩展函数,必须有一个接收者才能调用,先看普通类型:
fun testFunc(block: ()->Unit){
// 调用block
block.invoke()
}
再看扩展类型:
fun testFunc(block: T.()->Unit){
// 必须有个接收者来调用
val t = T()
t.block()
// 或者
block.invoke(t)
}
值得一提的是,T.(X, X, ....)->Unit
和(T, X, X, ....)->Unit
作为形参时是等价的,可以替换使用:
fun main() {
testFunc(a) // 注意这里a的类型
}
fun testFunc(block: String.(Int) -> Char) {
"hello".block(18)
block.invoke("hello", 18)
}
val a: (String, Int) -> Char = String::get
在Java中调用
先来看高阶函数,例如:
fun testFunc(block: (Int) -> String) {
block.invoke(18)
}
block参数是一个函数类型,被编译成Function接口的子接口,根据入参的个数会被编译成Funtion0、Function1、Function2等等。这些Function接口只有一个抽象方法invoke,入参类型和函数类型中的入参类型一致,出参也是一致的,也就是说,block参数类型等价于FuntionN类型,在Java中调用时,直接传入匿名内部类即可,也可以用lambda表达式。
在Java中调用编译器会给出提示,完整的调用方式如下:
TestKt.testFunc(new Function1<Integer, String>() {
@Override
public String invoke(Integer integer) {
return null;
}
});
再来看扩展函数,看这个例子:
fun testFunc(block: String.(Int) -> Char) {
}
上面提到过T.(X, X, ....)->Unit
和(T, X, X, ....)->Unit
是等价的,那么这个函数等价于:
fun testFunc(block: (String, Int) -> Char) {
}
其实又回到了上面的高阶函数调用方式,是一样的调用方式:
TestKt.testFunc(new Function2<String, Integer, Character>() {
@Override
public Character invoke(String s, Integer integer) {
return null;
}
});