Kotlin:高阶函数和Lambda表达式到底是什么?

kotlin中函数作为一等公民,成为独有的函数类型,在高阶函数中,既可作为参数传递,也可作为函数返回值。那么实际上,高阶函数到底是什么呢?
为了介绍高阶函数和Lambda表达式是什么,首先先简单引入下高阶函数:

高阶函数是将函数用作参数或返回值的函数。

简明扼要,简单写一个高阶函数:

/**
 * 参数类型包含函数类型
 */
fun lambdaParam(block: (Int) -> Unit) {
  block(2)
}

/**
 * 返回值为函数类型
 */
fun lambdaReturn(): (Int) -> Int {
  return {
    it * 2
  }
}
  • lambdaParam()函数将block函数作为传参,其中:
    1. block叫做函数名
    2. (Int) -> Unit叫做函数类型,也就是block的函数类型
    3. 通过block(Int)或者block.invoke(Int)调用传参
  • lambdaReturn()函数将(Int) -> Int作为函数的返回值,其中:
    1. (Int) -> Int是该高阶函数的返回值类型
    2. 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 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 = {"", "", "it", "invoke"})
static final class LambdaKt$lambdaReturn$1 extends Lambda implements Function1 {
  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 : Function {
  /** Invokes the function. */
  public operator fun invoke(): R
}
/** A function that takes 1 argument. */
public interface Function1 : Function {
  /** Invokes the function with the specified argument. */
  public operator fun invoke(p1: P1): R
}
/** A function that takes 2 arguments. */
public interface Function2 : Function {
  /** 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 : Function {
  /** Invokes the function with the specified arguments. */
  public operator fun invoke(p1: P1, p2: P2, p3: P3): R
}
.... ...

kotlin定义了一堆的FunctionX接口,

  1. 其中前面的P1 P2用来适配不同个数参数的函数,R代表函数的返回值,
    例如刚才上例中用的(Int) ->UnitFuncion1,也就是接收一个参数Int,返回值为Unit的函数
  2. 在每个FunctionX接口中还添加了一个invoke操作符重载方法,重载的也就是()这个操作符,因此我们在例子中才可以使用block(Int),实际上就是使用Function1().invoke(p1:P1)方法。
    所有的FunctionX接口都实现了Function接口,其实也就是函数返回值。

那么lambda表达式继承的Lambda类又是什么呢?继续跟踪:

package kotlin.jvm.internal
import java.io.Serializable
abstract class Lambda(override val arity: Int) : FunctionBase, Serializable {
  override fun toString(): String = Reflection.renderLambdaToString(this)
}

... ...

interface FunctionBase : Function {
  val arity: Int
}

原来也是实现了Function接口的一个抽象类。

Java调用kotlin高阶函数

java8之前:通过实现匿名接口类来调用kotlin的高阶函数:

LambdaKt.lambdaParam(new Function1() {
  @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方法。

你可能感兴趣的:(Kotlin:高阶函数和Lambda表达式到底是什么?)