第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號(hào)安全,請(qǐng)及時(shí)綁定郵箱和手機(jī)立即綁定
已解決430363個(gè)問題,去搜搜看,總會(huì)有你想問的

為什么 Kotlin 調(diào)用 java 時(shí)可以使用 Lambda

為什么 Kotlin 調(diào)用 java 時(shí)可以使用 Lambda

www說 2019-03-12 15:08:03
為什么 Kotlin 調(diào)用 java 時(shí)可以使用 Lambda
查看完整描述

2 回答

?
有只小跳蛙

TA貢獻(xiàn)1824條經(jīng)驗(yàn) 獲得超8個(gè)贊

1. Kotlin 中的 Lambda 表達(dá)式

如果你已經(jīng)開始使用 Koltin, 或者對(duì)它有過一些了解的話,那么一定對(duì)這種寫法并不陌生了:

// 代碼一:Kotlin 代碼view.setOnClickListener{println("click")}1234

它跟下面這段 Java 代碼是等價(jià)的:

// 代碼二:java 代碼view.setOnClickListener(new View.OnClickListener() {    @Overridepublic void onClick(View v) {System.out.println("click");}});1234567

和 Java8 一樣,Kotlin 是支持 Lambda 表達(dá)式的,如代碼一所示,就是 Lambda 的一個(gè)具體應(yīng)用。

可見,使用 lambda 減少了很多冗余,使代碼寫起來更簡(jiǎn)潔優(yōu)雅,讀起來也更順暢自然了。

但是,你有沒有想過,為什么 Kotlin 可以這樣寫,這里為什么可以使用 lambda ?

2. 為什么可以這么寫?

在 Kotlin 中,一個(gè) Lambda 就是一個(gè)匿名函數(shù)。

代碼一其實(shí)是對(duì)下面代碼三的簡(jiǎn)寫:

// 代碼三:Kotlin 代碼view.setOnClickListener({v -> println("click")})1234

之所以簡(jiǎn)寫成代碼一的樣子,是基于這兩點(diǎn)特性:

  • 如果 lambda 是一個(gè)函數(shù)的唯一參數(shù),那么調(diào)用這個(gè)函數(shù)時(shí)可以省略圓括號(hào)

  • 如果 lambda 所表示的匿名函數(shù)只有一個(gè)參數(shù),那么可以省略它的聲明以及->符號(hào)(默認(rèn)會(huì)用it來給省略的參數(shù)名命名)

  • OK,從代碼三的結(jié)構(gòu)中,能夠更清晰的看出,這里的 view.setOnClickListener 函數(shù)是接收了一個(gè) lambda 作為參數(shù)。而在 Kotlin 中,什么樣的函數(shù)才能把lambda(也即另一個(gè)函數(shù))作為參數(shù)呢?—— 對(duì),就是高階函數(shù)。

    什么是高階函數(shù)?

    高階函數(shù)是將函數(shù)用作參數(shù)或返回值的函數(shù)。

    這是 Kotlin 和 Java 的區(qū)別之一,java 中并沒有高階函數(shù)的支持(java8是有高階函數(shù)的)。當(dāng)我們?cè)?java 中需要用到類似的概念時(shí),通常的做法是傳遞一個(gè)匿名類作為參數(shù),然后實(shí)現(xiàn)其中的某些抽象方法 —— 就比如上面的代碼二。

    事實(shí)上,如果在 Android Studio 中,從 Kotlin 的代碼查看 view.setOnClickListener 函數(shù)的定義,就會(huì)發(fā)現(xiàn),看到的函數(shù)簽名就是一個(gè)高階函數(shù)的定義:

    函數(shù)簽名提示

    如上圖,所看到函數(shù)簽名是:

    public final fun setOnClickListener(l: ((v:View!)->Unit)!): Unit

    當(dāng)然,因?yàn)榉椒ㄊ窃?Java 中定義的,所以它也列出了 Java 的聲明,是這樣:

    public void setOnClickListener(OnClickListener l)

    我們知道,Kotlin 跟 Java 的很多類型都有差異,所以它們?cè)诨ハ嗾{(diào)用的時(shí),會(huì)有一個(gè)按照對(duì)應(yīng)關(guān)系的轉(zhuǎn)換。

    對(duì)于上面的對(duì) setOnClickListener 方法的轉(zhuǎn)換,別的地方都好理解,比較難懂的是,為什么會(huì)把參數(shù)從 OnClickListener 類型轉(zhuǎn)換成了 (View) -> Unit。

    (View) -> Unit 是一個(gè)函數(shù)類型,它表示這樣一個(gè)函數(shù):接收1個(gè)View類型的參數(shù),返回Unit。

    正是這個(gè)對(duì)參數(shù)類型的轉(zhuǎn)換,使得 setOnClickListener 方法在 Kotlin 中變成了一個(gè)高階函數(shù),這樣正是它之所以能夠使用 lambda 作為參數(shù)的原因。

    而這種轉(zhuǎn)換,就是我們題目中所說到這篇文章的主角 —— SAM 轉(zhuǎn)換 (Single Abstract Method Conversions)。

    3. 什么是 SAM 轉(zhuǎn)換?

    好吧,說了這么多,終于到正題了。

    SAM 轉(zhuǎn)換,即 Single Abstract Method Conversions,就是對(duì)于只有單個(gè)非默認(rèn)抽象方法接口的轉(zhuǎn)換 —— 對(duì)于符合這個(gè)條件的接口(稱之為 SAM Type ),在 Kotlin 中可以直接用 Lambda 來表示 —— 當(dāng)然前提是 Lambda 的所表示函數(shù)類型能夠跟接口的中方法相匹配。

    而 OnClickListener 在 java 中的定義是這樣的:

  • // 代碼四:OnClickListener 接口在 java 中的定義public interface OnClickListener {    void onClick(View v);


  • }1234


  • —— 恰好它就是一個(gè)符合條件的 SAM Type,onClick 函數(shù)的類型即是 (View) -> Unit。所以,在 Kotlin 中,能夠用 lambda 表達(dá)式 { println("click")} 來代替 OnClickListener 作為 setOnClickListener 函數(shù)的參數(shù)。

    4. SAM 轉(zhuǎn)換的歧義消除

    SAM 轉(zhuǎn)換的存在,使得我們?cè)?Kotlin 中調(diào)用 java 的時(shí)候能夠更得心應(yīng)手了,它在大部分的時(shí)間都能工作的很好。

    當(dāng)然,也偶爾會(huì)有例外,比如,考慮下面的這段代碼:

  • // 代碼五public class TestSAM {


  •    SamType1 sam1,;


  •    SamType2 sam2,;    public void setSam(SamType1 sam1) {        this.sam1 = sam1;


  •    }    public void setSam(SamType2 sam2) {        this.sam2 = sam2;


  •    }    public interface SamType1 {        void doSomething(int value);


  •    }    public interface SamType2 {        void doSomething2(int value);


  •    }


  • }123456789101112131415161718


  • —— TestSAM 有兩個(gè)重載的 setSam 方法,—— 并且它們的參數(shù)( SamType1、SamType2 )都是 SAM Type 的接口。—— 并且 SamType1 跟 SamType2 的唯一抽象方法的函數(shù)類型都是 (Int) -> Unit 。

    o(╯□╰)o

    這種情況比較吊軌,但是還有有可能會(huì)出現(xiàn)的。這時(shí)候,如果在 Kotlin 中直接使用代碼一類似的方式,就會(huì)報(bào)錯(cuò)了:

  • // 代碼六:kotlin中調(diào)用,這段代碼是編譯不過的TestSAM().setSam {


  •    println("dodo")  


  • }1234


  • 會(huì)提示這里歧義,編譯器不知道這個(gè) Lambda 代表是 SamType1 跟 SamType2 中的哪一個(gè)接口。

    解決的辦法就是手動(dòng)標(biāo)明 Lambda 需要代替的接口類型,有兩種方式可以來標(biāo)明:

  • // 代碼七: 歧義消除// 方式一TestSAM().setSam (SamType1 { println("dodo")  })


  • // 方式二TestSAM().setSam ({ println("dodo") } as SamType1)12345


  • 當(dāng)然,也還有一種方法是不再使用 SAM 轉(zhuǎn)換的機(jī)制,而是直接使用一個(gè) SamType1 的實(shí)例作為參數(shù):

  • // 代碼八: 使用一個(gè)實(shí)現(xiàn)接口的匿名類作為參數(shù)TestSAM().setSam(object : TestSAM.SamType1 {    override fun doSomething(value: Int) {


  •        println("dodo")


  •    }


  • })123456


  • 這種方法當(dāng)然也是可以的,只是跟 lambda 相比起來,就顯得不那么優(yōu)雅了(優(yōu)雅很重要?。。。?。

    5. SAM 轉(zhuǎn)換的限制

    SAM 轉(zhuǎn)換的限制主要有兩點(diǎn) :

    5.1 只支持 java

    即只適用與 Kotlin 中對(duì) java 的調(diào)用,而不支持對(duì) Kotlin 的調(diào)用

    官方的解釋是 Kotlin 本身已經(jīng)有了函數(shù)類型和高階函數(shù)等支持,所以不需要了再去轉(zhuǎn)換了。

    如果你想使用類似的需要用 lambda 做參數(shù)的操作,應(yīng)該自己去定義需要指定函數(shù)類型的高階函數(shù)。

    5.2 只支持接口,不支持抽象類。

    這個(gè)官方?jīng)]有多做解釋。

    我想大概是為了避免混亂吧,畢竟如果支持抽象類的話,需要做強(qiáng)轉(zhuǎn)的地方就太多了。而且抽象類本身是允許有很多邏輯代碼在內(nèi)部的,直接簡(jiǎn)寫成一個(gè) Lambda 的話,如果出了問題去定位錯(cuò)誤的難度也加大了很多。

    6. 總結(jié)

    OK,講完了??偨Y(jié)起來就是 SAM 轉(zhuǎn)換就是 kotlin 在調(diào)用 java 代碼時(shí)能使用 Lambda 的原因。了解了其原理,能夠讓我們?cè)趯懘a更自如,在偶爾出問題的時(shí)候也能更好更快地解決。



查看完整回答
反對(duì) 回復(fù) 2019-03-19
?
慕神8447489

TA貢獻(xiàn)1780條經(jīng)驗(yàn) 獲得超1個(gè)贊

kotlin java都是jvm上的語言,對(duì)于jvm來說,沒有java,沒有kotlin,這些語言,只有一個(gè)特定格式的 class文件,只要這個(gè)class文件的格式滿足jvm的格式標(biāo)準(zhǔn),jvm就可以運(yùn)行這份class文件,java 跟kotlin的語法不一樣,但是他們有不同的編譯器,不同的編譯器,會(huì)把對(duì)應(yīng)的源文件,轉(zhuǎn)換成標(biāo)準(zhǔn)的class格式,從我們看源代碼的方式來看,代碼是不一樣的,但是從jvm運(yùn)行的角度來看,都是標(biāo)準(zhǔn)的class文件格式,kotlin使用Lambda的語法,經(jīng)過編譯器的時(shí)候,會(huì)把這種語法轉(zhuǎn)換成標(biāo)準(zhǔn)的class格式就可以了,

查看完整回答
反對(duì) 回復(fù) 2019-03-19
  • 2 回答
  • 0 關(guān)注
  • 976 瀏覽

添加回答

舉報(bào)

0/150
提交
取消
微信客服

購(gòu)課補(bǔ)貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動(dòng)學(xué)習(xí)伙伴

公眾號(hào)

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號(hào)