kotlin中函數(shù)作為一等公民枯芬,成為獨(dú)有的函數(shù)類型论笔,在高階函數(shù)中采郎,既可作為參數(shù)傳遞,也可作為函數(shù)返回值狂魔。那么實際上蒜埋,高階函數(shù)到底是什么呢?
為了介紹高階函數(shù)和Lambda表達(dá)式是什么最楷,首先先簡單引入下高階函數(shù):
高階函數(shù)是將函數(shù)用作參數(shù)或返回值的函數(shù)整份。
簡明扼要,簡單寫一個高階函數(shù):
/**
* 參數(shù)類型包含函數(shù)類型
*/
fun lambdaParam(block: (Int) -> Unit) {
block(2)
}
/**
* 返回值為函數(shù)類型
*/
fun lambdaReturn(): (Int) -> Int {
return {
it * 2
}
}
-
lambdaParam()
函數(shù)將block
函數(shù)作為傳參籽孙,其中:-
block
叫做函數(shù)名 -
(Int) -> Unit
叫做函數(shù)類型烈评,也就是block
的函數(shù)類型 - 通過
block(Int)
或者block.invoke(Int)
調(diào)用傳參
-
-
lambdaReturn()
函數(shù)將(Int) -> Int
作為函數(shù)的返回值,其中:-
(Int) -> Int
是該高階函數(shù)的返回值類型 -
return
后的{it*2}
閉包犯建,實際是個lambda表達(dá)式讲冠,也就是匿名函數(shù),當(dāng)其僅有一個參數(shù)時胎挎,可以忽略不寫用it
代替沟启,并且閉包內(nèi)最后一行為其返回值
-
上面解釋了高階函數(shù)的簡單用法,那么為什么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表達(dá)式,實際上是繼承自Lambda
類称杨,實現(xiàn)了一個Function1
接口肌毅,而且其內(nèi)部的invoke
方法,默認(rèn)有個it
傳參姑原,所以使用時不需要寫出參數(shù)名和類型悬而,實質(zhì)上,就跟Java實現(xiàn)匿名內(nèi)部類的做法一樣锭汛。
那么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
用來適配不同個數(shù)參數(shù)的函數(shù)唤殴,R
代表函數(shù)的返回值般婆,
例如剛才上例中用的(Int) ->Unit
是Funcion1
,也就是接收一個參數(shù)Int
朵逝,返回值為Unit
的函數(shù) - 在每個
FunctionX
接口中還添加了一個invoke
操作符重載方法蔚袍,重載的也就是()
這個操作符,因此我們在例子中才可以使用block(Int)
配名,實際上就是使用Function1().invoke(p1:P1)
方法啤咽。
所有的FunctionX
接口都實現(xiàn)了Function<R>
接口晋辆,其實也就是函數(shù)返回值。
那么lambda表達(dá)式繼承的Lambda
類又是什么呢闰蚕?繼續(xù)跟蹤:
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
}
原來也是實現(xiàn)了Function
接口的一個抽象類栈拖。
Java調(diào)用kotlin高階函數(shù)
java8之前:通過實現(xiàn)匿名接口類來調(diào)用kotlin的高階函數(shù):
LambdaKt.lambdaParam(new Function1<Integer, Unit>() {
@Override
public Unit invoke(Integer integer) {
return null;
}
});
java8之后:由于java8支持了SAM的lambda表達(dá)式连舍,而由于kotlin中的lambda表達(dá)式本身實現(xiàn)的Function接口也是只有一個方法没陡,因此同kotlin中調(diào)用相同:
LambdaKt.lambdaParam(i-> null);
總結(jié)
- kotlin所謂的高階函數(shù)實際是借由
Function
接口的一個函數(shù),傳的是Function
實現(xiàn)類的引用索赏,用的是其匿名內(nèi)部類實現(xiàn)盼玄。 - 所謂的Lambda表達(dá)式,實則是kotlin當(dāng)中的匿名函數(shù)潜腻,也就是java中的匿名內(nèi)部類的實現(xiàn)埃儿。
- 并且
Function
接口中重載了操作符()
,使我們使用其實現(xiàn)類()就是在調(diào)用其invoke
方法融涣。