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

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ù)作為傳參籽孙,其中:
    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}閉包犯建,實際是個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接口,

  1. <in P1...,out R>其中前面的P1 P2用來適配不同個數(shù)參數(shù)的函數(shù)唤殴,R代表函數(shù)的返回值般婆,
    例如剛才上例中用的(Int) ->UnitFuncion1,也就是接收一個參數(shù)Int朵逝,返回值為Unit的函數(shù)
  2. 在每個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方法融涣。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末童番,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子威鹿,更是在濱河造成了極大的恐慌剃斧,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忽你,死亡現(xiàn)場離奇詭異幼东,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)科雳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門根蟹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人糟秘,你說我怎么就攤上這事简逮。” “怎么了尿赚?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵散庶,是天一觀的道長。 經(jīng)常有香客問我吼畏,道長督赤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任泻蚊,我火速辦了婚禮躲舌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘性雄。我一直安慰自己没卸,他們只是感情好羹奉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著约计,像睡著了一般诀拭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上煤蚌,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天耕挨,我揣著相機(jī)與錄音,去河邊找鬼尉桩。 笑死筒占,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蜘犁。 我是一名探鬼主播翰苫,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼这橙!你這毒婦竟也來了奏窑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤屈扎,失蹤者是張志新(化名)和其女友劉穎埃唯,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體助隧,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筑凫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了并村。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巍实。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖哩牍,靈堂內(nèi)的尸體忽然破棺而出棚潦,到底是詐尸還是另有隱情,我是刑警寧澤膝昆,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布丸边,位于F島的核電站,受9級特大地震影響荚孵,放射性物質(zhì)發(fā)生泄漏妹窖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一收叶、第九天 我趴在偏房一處隱蔽的房頂上張望骄呼。 院中可真熱鬧,春花似錦、人聲如沸蜓萄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嫉沽。三九已至辟犀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绸硕,已是汗流浹背堂竟。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留臣咖,地道東北人跃捣。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像夺蛇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子酣胀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355