當前位置:
首頁 > 最新 > 為什麼 Kotlin 調用 java 時可以使用 Lambda?——Kotlin中SAM 轉換機制詳解

為什麼 Kotlin 調用 java 時可以使用 Lambda?——Kotlin中SAM 轉換機制詳解

先放個目錄:

1. Kotlin 中的 Lambda 表達式

2. 為什麼可以這麼寫?

3. 什麼是 ?

4. SAM 轉換的歧義消除

5. SAM 轉換的限制

5.1 只支持 java

5.2 只支持介面,不支持抽象類。

6. 總結

7. 關於作者

1. Kotlin 中的 Lambda 表達式

如果你已經開始使用 Koltin, 或者對它有過一些了解的話,那麼一定對這種寫法並不陌生了:

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

↑它跟下面這段 Java 代碼是等價的↓:

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

和 Java8 一樣,Kotlin 是支持 Lambda 表達式的,如代碼一所示,就是 Labmda 的一個具體應用。

可見,使用 Lambda 減少了很多冗餘,使代碼寫起來更簡潔優雅,讀起來也更順暢自然了。

但是,你有沒有想過,為什麼 Kotlin 可以這樣寫,這裡為什麼可以使用 Lambda 作為 setOnClickListener 的參數?

2. 為什麼可以這麼寫?

在 Kotlin 中,一個 Lambda 就是一個匿名函數。

代碼一其實是對下面代碼三的簡寫:

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

之所以簡寫成代碼一的樣子,是基於這兩點特性:

1. 如果 Lambda 是一個函數的唯一參數,那麼調用這個函數時可以省略圓括弧

2. 如果 Lambda 所表示的匿名函數只有一個參數,那麼可以省略它的聲明以及 符號(默認會用 來給省略的參數名命名)

OK,從代碼三的結構中,能夠更清晰的看出,這裡的 view.setOnClickListener 函數是接收了一個 Lambda 作為參數。而在 Kotlin 中,什麼樣的函數才能把Lambda(也即另一個函數)作為參數呢?—— 對,就是 。

什麼是 ?

高階函數是將函數用作參數或返回值的函數。

這是 Kotlin 和 Java 的區別之一,java 中並沒有高階函數的支持。當我們在 java 中需要用到類似的概念時,通常的做法是傳遞一個匿名類作為參數,然後實現其中的某些抽象方法 —— 就比如上面的代碼二。

事實上,如果在 Android Studio 中,從 Kotlin 的代碼查看 view.setOnClickListener 函數的定義,就會發現,看到的函數簽名就是一個高階函數的定義:

如上圖,所看到函數簽名是:

當然,因為方法是在 Java 中定義的,所以它也列出了 Java 的聲明,是這樣:

我們知道,Kotlin 跟 Java 的很多類型都有差異,所以它們在互相調用的時,會有一個按照對應關係的轉換。

對於上面的對 setOnClickListener 方法的轉換,別的地方都好理解,比較難懂的是,為什麼會把參數類型從 轉換成了 。

是一個函數類型,它表示這樣一個函數:接收1個View類型的參數,返回Unit

插一句:什麼是 ?

相對於 Java, Kotlin 中多了一個 的概念,即函數是有類型的,一個函數的類型用它的所有的參數的類型和它的函數值的類型共同表示。比如 ,表示這樣的函數類型:第一個參數是 String ,第二個參數 Int ,返回值也是 Int 類型。

正是這個對參數類型的轉換,使得 setOnClickListener 方法在 Kotlin 中變成了一個高階函數,這樣正是它之所以能夠使用 Lambda 作為參數的原因。

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

3. 什麼是 ?

好吧,說了這麼多,終於到正題了。

SAM 轉換,即 Single Abstract Method Conversions,就是對於只有單個非默認抽象方法介面的轉換 —— 對於符合這個條件的介面(稱之為 ),在 Kotlin 中可以直接用 Lambda 來表示 —— 當然前提是 Lambda 的所表示函數類型能夠跟介面的中方法相匹配。

我們知道, 在 java 中的定義是這樣的:

// 代碼四:OnClickListener 介面在 java 中的定義 public interface OnClickListener { void onClick(View v); }

—— 可以看到,恰好它就是一個符合條件的 ,onClick 函數的類型即是 。所以,在 Kotlin 中,能夠用 Lambda 表達式 來代替 作為 setOnClickListener 函數的參數。

插一句: 其實並不是 Kotlin 獨創的,在 Java8 、Scala 中已經有了這種機制。

4. SAM 轉換的歧義消除

SAM 轉換的存在,使得我們在 Kotlin 中調用 java 的時候能夠更得心應手了,它在大部分的時間都能工作的很好。

當然,也偶爾會有例外,比如,考慮下面的這段代碼:

// 代碼五 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); } }

這段代碼中有如下特徵:

—— TestSAM 有兩個重載的 setSam 方法,

—— 並且它們的參數( SamType1、SamType2 )都是 的介面。

—— 並且 SamType1 跟 SamType2 的唯一抽象方法的函數類型都是 。

這種情況比較弔詭,但是還有有可能會出現的。這時候,如果在 Kotlin 中直接使用代碼一類似的方式,就會報錯了:

// 代碼六:kotlin中調用,這段代碼是編譯不過的 TestSAM().setSam { println("dodo") }

會提示這裡歧義,編譯器不知道這個 Lambda 代表是 SamType1 跟 SamType2 中的哪一個介面。

解決的辦法就是手動標明 Lambda 需要代替的介面類型,有兩種方式可以來標明:

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

當然,也還有一種方法是不再使用 SAM 轉換的機制,而是直接使用一個 SamType1 的實例作為參數:

// 代碼八: 使用一個實現介面的匿名類作為參數 TestSAM().setSam(object:TestSAM.SamType1{ override fun doSomething(value: Int){ println("dodo") } })

這種方法當然也是可以的,只是跟 Lambda 相比起來,就顯得不那麼優雅了(優雅重要!!!)。

5. SAM 轉換的限制

Kotin 中 SAM 轉換的限制主要有兩點:

5.1 只支持 java

即只適用與 Kotlin 中對 java 的調用,而不支持對 Kotlin 的調用

官方的解釋是 Kotlin 本身已經有了 和 等支持,所以不需要了再去轉換了。

如果你想使用類似的需要用 Lambda 做參數的操作,應該自己去定義需要指定函數類型的高階函數。

5.2 只支持介面,不支持抽象類。

這個官方沒有多做解釋。

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

6. 總結

OK,講完了。

總結起來就是 就是 kotlin 在調用 java 代碼時能使用 Lambda 的原因。了解了其原理,能夠讓我們在寫代碼更自如,在偶爾出問題的時候也能更好更快地解決。

7. 關於作者

https://github.com/barryhappy

http://www.jianshu.com/users/e4607fd59d0d

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 推酷 的精彩文章:

鎚子銷售額大增3006%,小米榮耀死磕第1,京東618排行榜上,最受傷的竟是魅族
新產品管理:神秘的「戰略」到底是什麼?
對待產品項目,PM如何巨細兼顧?
Java進階自測:面向對象基礎知識掌握了嗎?
景馳、圖森和小馬智行,連續三家中國自動駕駛公司在美路測獲批

TAG:推酷 |

您可能感興趣

JSONObject 轉換時出錯 InvocationTargetException
Kiwi for Gmail是Windows應用程序,可以轉換體驗
Nvidia 圖片風格轉換工具——FastPhotoStyle
TensorFlow新功能「AutoGraph」:將Python轉換為計算圖
DataURL, Blob, File, Image之間的關係與轉換
CodeWarrior IDE使用Tips-使用burner將elf文件轉換生成HEX和BIN文件的方法和步驟詳解
Project Parfait將Photoshop文件轉換為代碼
使用 pandoc 將 Markdown 轉換為格式化文檔
使用 Handbrake 轉換視頻
在 Linux 中使用 SoundConverter 輕鬆轉換音頻文件格式
?可以給你帶來轉換率的Listing優化之A+ Content
類型轉換運算符 運算符重載 operator new operator delete
將設計稿自動轉換為代碼的神經網路 Screenshot-to-code-in-Keras
Puppemoji應用將用戶轉換為類似Animoji動漫人物
承襲 Top 3 精神 大玩色塊轉換的 Air Jordan 1 『Gold Toe
CYBERGadget將推出Switch用NGC手柄轉換器
蘋果開放Lightning與3.5mm轉換線認證 但USB-C仍沒有
short數組保存unicode編碼和unicode編碼轉換成shrot數組
HyperDrive轉換器 MacBook Pro擴展塢,多功能擴展更專業
Spatial MRX可將手機轉換為全息AR