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<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接口,

  1. <in P1...,out R>其中前面的P1 P2用来适配不同个数参数的函数,R代表函数的返回值,
    例如刚才上例中用的(Int) ->UnitFuncion1,也就是接收一个参数Int,返回值为Unit的函数
  2. 在每个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方法。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,248评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,681评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,443评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,475评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,458评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,185评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,451评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,112评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,609评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,083评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,163评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,803评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,357评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,357评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,590评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,636评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,925评论 2 344