kotlin中函数作为一等公民,成为独有的函数类型,在高阶函数中,既可作为参数传递,也可作为函数返回值。那么实际上,高阶函数到底是什么呢?
为了介绍高阶函数和Lambda表达式是什么,首先先简单引入下高阶函数:
高阶函数是将函数用作参数或返回值的函数。
简明扼要,简单写一个高阶函数:
/**
* 参数类型包含函数类型
*/
fun lambdaParam(block: (Int) -> Unit) {
block(2)
}
/**
* 返回值为函数类型
*/
fun lambdaReturn(): (Int) -> Int {
return {
it * 2
}
}
-
lambdaParam()
函数将block
函数作为传参,其中:-
block
叫做函数名 -
(Int) -> Unit
叫做函数类型,也就是block
的函数类型 - 通过
block(Int)
或者block.invoke(Int)
调用传参
-
-
lambdaReturn()
函数将(Int) -> Int
作为函数的返回值,其中:-
(Int) -> Int
是该高阶函数的返回值类型 -
return
后的{it*2}
闭包,实际是个lambda表达式,也就是匿名函数,当其仅有一个参数时,可以忽略不写用it
代替,并且闭包内最后一行为其返回值
-
上面解释了高阶函数的简单用法,那么为什么kotlin能这么用,而java不行呢?使用JD-GUI反编译成java代码看一下:
public static final void lambdaParam(@NotNull Function1 block) {
Intrinsics.checkParameterIsNotNull(block, "block");
block.invoke(Integer.valueOf(2));
}
@NotNull
public static final Function1<Integer, Integer> lambdaReturn() {
return LambdaKt$lambdaReturn$1.INSTANCE;
}
@Metadata(mv = {1, 1, 16}, bv = {1, 0, 3}, k = 3, d1 = {"\000\n\n\000\n\002\020\b\n\002\b\002\020\000\032\0020\0012\006\020\002\032\0020\001H\n\006\002\b\003"}, d2 = {"<anonymous>", "", "it", "invoke"})
static final class LambdaKt$lambdaReturn$1 extends Lambda implements Function1<Integer, Integer> {
public static final LambdaKt$lambdaReturn$1 INSTANCE = new LambdaKt$lambdaReturn$1();
public final int invoke(int it) {
return it * 2;
}
LambdaKt$lambdaReturn$1() {
super(1);
}
}
是不是恍然大悟,哦,这就是Java的接口嘛。所谓的lambda表达式,实际上是继承自Lambda
类,实现了一个Function1
接口,而且其内部的invoke
方法,默认有个it
传参,所以使用时不需要写出参数名和类型,实质上,就跟Java实现匿名内部类的做法一样。
那么Function1
接口是什么?跟踪下:
package kotlin.jvm.functions
/** A function that takes 0 arguments. */
public interface Function0<out R> : Function<R> {
/** Invokes the function. */
public operator fun invoke(): R
}
/** A function that takes 1 argument. */
public interface Function1<in P1, out R> : Function<R> {
/** Invokes the function with the specified argument. */
public operator fun invoke(p1: P1): R
}
/** A function that takes 2 arguments. */
public interface Function2<in P1, in P2, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2): R
}
/** A function that takes 3 arguments. */
public interface Function3<in P1, in P2, in P3, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3): R
}
.... ...
kotlin定义了一堆的FunctionX
接口,
-
<in P1...,out R>
其中前面的P1 P2
用来适配不同个数参数的函数,R
代表函数的返回值,
例如刚才上例中用的(Int) ->Unit
是Funcion1
,也就是接收一个参数Int
,返回值为Unit
的函数 - 在每个
FunctionX
接口中还添加了一个invoke
操作符重载方法,重载的也就是()
这个操作符,因此我们在例子中才可以使用block(Int)
,实际上就是使用Function1().invoke(p1:P1)
方法。
所有的FunctionX
接口都实现了Function<R>
接口,其实也就是函数返回值。
那么lambda表达式继承的Lambda
类又是什么呢?继续跟踪:
package kotlin.jvm.internal
import java.io.Serializable
abstract class Lambda<out R>(override val arity: Int) : FunctionBase<R>, Serializable {
override fun toString(): String = Reflection.renderLambdaToString(this)
}
... ...
interface FunctionBase<out R> : Function<R> {
val arity: Int
}
原来也是实现了Function
接口的一个抽象类。
Java调用kotlin高阶函数
java8之前:通过实现匿名接口类来调用kotlin的高阶函数:
LambdaKt.lambdaParam(new Function1<Integer, Unit>() {
@Override
public Unit invoke(Integer integer) {
return null;
}
});
java8之后:由于java8支持了SAM的lambda表达式,而由于kotlin中的lambda表达式本身实现的Function接口也是只有一个方法,因此同kotlin中调用相同:
LambdaKt.lambdaParam(i-> null);
总结
- kotlin所谓的高阶函数实际是借由
Function
接口的一个函数,传的是Function
实现类的引用,用的是其匿名内部类实现。 - 所谓的Lambda表达式,实则是kotlin当中的匿名函数,也就是java中的匿名内部类的实现。
- 并且
Function
接口中重载了操作符()
,使我们使用其实现类()就是在调用其invoke
方法。