Kotlin:高階函數(shù)和Lambda表達(dá)式到底是什么?

kotlin中函數(shù)作為一等公民,成為獨(dú)有的函數(shù)類型,在高階函數(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ù)作為傳參,其中:
    1. block叫做函數(shù)名
    2. (Int) -> Unit叫做函數(shù)類型,也就是block的函數(shù)類型
    3. 通過block(Int)或者block.invoke(Int)調(diào)用傳參
  • lambdaReturn()函數(shù)將(Int) -> Int作為函數(shù)的返回值,其中:
    1. (Int) -> Int是該高階函數(shù)的返回值類型
    2. return后的{it*2}閉包,實(shí)際是個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á)式,實(shí)際上是繼承自Lambda類,實(shí)現(xiàn)了一個Function1接口,而且其內(nèi)部的invoke方法,默認(rèn)有個it傳參,所以使用時不需要寫出參數(shù)名和類型,實(shí)質(zhì)上,就跟Java實(shí)現(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接口,

  1. <in P1...,out R>其中前面的P1 P2用來適配不同個數(shù)參數(shù)的函數(shù),R代表函數(shù)的返回值,
    例如剛才上例中用的(Int) ->UnitFuncion1,也就是接收一個參數(shù)Int,返回值為Unit的函數(shù)
  2. 在每個FunctionX接口中還添加了一個invoke操作符重載方法,重載的也就是()這個操作符,因此我們在例子中才可以使用block(Int),實(shí)際上就是使用Function1().invoke(p1:P1)方法。
    所有的FunctionX接口都實(shí)現(xiàn)了Function<R>接口,其實(shí)也就是函數(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
}

原來也是實(shí)現(xiàn)了Function接口的一個抽象類。

Java調(diào)用kotlin高階函數(shù)

java8之前:通過實(shí)現(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á)式本身實(shí)現(xiàn)的Function接口也是只有一個方法,因此同kotlin中調(diào)用相同:

LambdaKt.lambdaParam(i-> null);

總結(jié)

  • kotlin所謂的高階函數(shù)實(shí)際是借由Function接口的一個函數(shù),傳的是Function實(shí)現(xiàn)類的引用,用的是其匿名內(nèi)部類實(shí)現(xiàn)。
  • 所謂的Lambda表達(dá)式,實(shí)則是kotlin當(dāng)中的匿名函數(shù),也就是java中的匿名內(nèi)部類的實(shí)現(xiàn)。
  • 并且Function接口中重載了操作符(),使我們使用其實(shí)現(xiàn)類()就是在調(diào)用其invoke方法。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容