關(guān)于Kotlin中SAM轉(zhuǎn)換的那些事
前言
隨著 Kotlin 1.4 正式發(fā)布,關(guān)于 SAM 轉(zhuǎn)換的一些問(wèn)題就可以蓋棺定論了。因?yàn)檫@里要講的都是些舊的東西,所以這是一篇灌水文。
Kotlin對(duì)SAM轉(zhuǎn)換的支持情況
在 1.4 發(fā)布之前,經(jīng)常有新人在群里提出關(guān)于 SAM 轉(zhuǎn)換的問(wèn)題。
為了說(shuō)明這個(gè)問(wèn)題,要分成幾個(gè)情況來(lái)討論。
我們需要區(qū)分這個(gè)接口是Java接口還是Kotlin接口:
// 這是Java
interface JavaSome {
void some();
}
// 這是Kotlin
interface KotlinSome {
fun some()
}
以及區(qū)分在Java還是Kotlin里使用該接口:
// 這是Java, ISome是一個(gè)接口
void useSome(ISome some) {}
// 這是Kotlin, ISome是一個(gè)接口
fun useSome(some: ISome) {}
兩兩相乘,我們就需要對(duì)4種情況進(jìn)行討論。當(dāng)然,useSome 函數(shù)都是在 Kotlin 里調(diào)用。
1、Java接口,Java使用
// Java
void useSome(JavaSome some) {}
// Kotlin
useSome {} // OK
這種情況下的 SAM 轉(zhuǎn)換,是自古以來(lái) Kotlin 就支持的。
2、Java接口,Kotlin使用
// Kotlin
fun useSome(some: JavaSome) {}
useSome {} // 能否編譯成功跟Kotlin版本和編譯器參數(shù)有關(guān)
Kotlin 1.2 以及更舊版本不支持這種情況下的SAM轉(zhuǎn)換。
Kotlin 1.3 版本,Kotlin 官方團(tuán)隊(duì)發(fā)現(xiàn)他們寫的那堆類型推斷算法是一座“屎山”,于是重新寫了套新的類型推斷算法,作為默認(rèn)關(guān)閉的實(shí)驗(yàn)性特性加入了 1.3 版本。新的類型推斷算法支持這種情況下的SAM轉(zhuǎn)換,不過(guò)需要手動(dòng)傳入編譯器參數(shù)來(lái)開(kāi)啟這個(gè)功能。
Kotlin 1.4 版本,由于新的類型推斷算法已經(jīng)默認(rèn)開(kāi)啟,所以這種情況下可以進(jìn)行SAM轉(zhuǎn)換。
3、Kotlin接口,Kotlin使用
// Kotlin
fun useSome(some: KotlinSome) {}
useSome {} // 編譯錯(cuò)誤!
這就是廣為人知、為人詬病的垃圾 Kotlin 不支持 SAM 轉(zhuǎn)換的情況。
在 Kotlin 1.4 版本,你需要在接口前加上關(guān)鍵字 fun,讓它成為一個(gè) fun interface 才能享受到 SAM 轉(zhuǎn)換。
// Kotlin
fun interface KotlinSome {
fun some()
}
fun useSome(some: KotlinSome) {}
useSome {} // OK
當(dāng)然 1.3 版本就別想了,老老實(shí)實(shí)升級(jí)吧。
4、Kotlin接口,Java使用
// Java
void useSome(KotlinSome some) {}
// Kotlin
useSome {} // 需要是 fun interface
非常少見(jiàn)。
和上面的第三種情況一樣,這需要 Kotlin 1.4 版本的 fun interface 才能進(jìn)行 SAM 轉(zhuǎn)換。
5、帶有suspend函數(shù)的Kotlin接口
四天王有五個(gè)人不是常識(shí)么
fun interface Some {
suspend fun some()
}
fun useSome(some: KotlinSome) {}
useSome {} // 嘻嘻
在 Kotlin 1.4 的測(cè)試版(里程碑版、RC版),可以編譯成功,但是運(yùn)行起來(lái)會(huì)炸。原因在于 Kotlin 官方團(tuán)隊(duì)并沒(méi)有寫好針對(duì)這種情況的代碼生成(codegen)。于是在 Kotlin 1.4 正式版,他們就 ban 掉了這樣的代碼,不允許 fun interface 擁有抽象 suspend 函數(shù)。
6、一些舊版本的bug
最經(jīng)典的是那個(gè)安卓的LiveData的某個(gè)函數(shù):
val liveData = MutableLiveData<Int>()
liveData.observe({ lifecycleOwner.lifecycle }, Observer { invokeMyMethod(it) })
// 第二個(gè)參數(shù)無(wú)法進(jìn)行SAM轉(zhuǎn)換
詳見(jiàn)KT-14984。
新的類型推斷算法修正了這個(gè)bug。
SAM Constructor
在 1.3 以及更早的版本,針對(duì)上面所說(shuō)的第二種情況,可以這樣使用:
// Kotlin
fun useSome(some: JavaSome) {}
useSome(JavaSome {})
想必各位過(guò)來(lái)人都知道這樣的寫法。
這里 JavaSome {},lambda 表達(dá)式前面的那個(gè) JavaSome 就是所謂的 SAM 構(gòu)造器(SAM constructor),或者說(shuō)是 SAM 適配器(SAM adapter)。
在現(xiàn)在 1.4 版本里,SAM constructor 已經(jīng)沒(méi)什么用了,主要用途是“憑空捏出”一個(gè) SAM 接口的實(shí)例:
val ktSome = KotlinSome {} // 需要是 fun interface
val javaSome = JavaSome {}
// 錯(cuò)誤用法
// val ktSome: KotlinSome = {}
// val javaSome: JavaSome = {}
SAM constructor 可以理解為編譯器為 SAM 接口生成了一個(gè)如下所示的輔助函數(shù),但是實(shí)際上這個(gè)函數(shù)并不存在。
// 這是Java
interface JavaSome {
void some();
}
// 實(shí)際上并不存在的輔助函數(shù)
inline fun JavaSome(block: () -> Unit): JavaSome {
return 編譯器的魔法
}
然后就有一些鮮為人知的用法,比如說(shuō)這樣:
// Kotlin
val lambda: () -> Unit = { println("test") }
val kepa: JavaSome = JavaSome(lambda) // 嘻嘻
kepa.some() // 輸出 test
上面這段代碼確實(shí)是可以跑的。
甚至是這樣:
val lambda: () -> Unit = { println("test") }
val some: KFunction1<() -> Unit, JavaSome> = ::JavaSome // 嘻嘻
val kepa: JavaSome = some.invoke(lambda)
kepa.some()
這段代碼 IDEA 不會(huì)提示錯(cuò)誤,但是會(huì)編譯失敗。
表面上看確實(shí)有這個(gè)輔助函數(shù),所以這樣的代碼可以通過(guò) Kotlin 編譯器前端的檢查。但是實(shí)際上編譯器的后端并沒(méi)有辦法針對(duì)這樣的情況進(jìn)行代碼生成,徹底懵逼了,boom!
你學(xué)到了什么
- 一些無(wú)用的歷史知識(shí)
- 關(guān)于 SAM constructor 的冷知識(shí)
本文完。
總結(jié)
到此這篇關(guān)于關(guān)于Kotlin中SAM轉(zhuǎn)換的文章就介紹到這了,更多相關(guān)Kotlin中SAM轉(zhuǎn)換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Pearson相關(guān)系數(shù)和Spearman相關(guān)系數(shù)的區(qū)別及說(shuō)明
這篇文章主要介紹了Pearson相關(guān)系數(shù)和Spearman相關(guān)系數(shù)的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
正則給header的冒號(hào)兩邊參數(shù)添加單引號(hào)(Python請(qǐng)求用)
這篇文章主要介紹了正則給header的冒號(hào)兩邊參數(shù)添加單引號(hào)(Python請(qǐng)求用)的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08
pdf論文中python畫的圖Type 3 fonts字體不兼容的解決方案
這篇文章主要介紹了pdf論文中python畫的圖Type 3 fonts字體不兼容的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04
深入解析PYTHON?虛擬機(jī)令人拍案叫絕的字節(jié)碼設(shè)計(jì)
這篇文章主要為大家介紹了PYTHON虛擬機(jī)中令人拍案叫絕的字節(jié)碼設(shè)計(jì)深入詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
Python中字符串的常見(jiàn)操作技巧總結(jié)
這篇文章主要介紹了Python中字符串的常見(jiàn)操作技巧,結(jié)合實(shí)例形式總結(jié)分析了Python針對(duì)字符串的反轉(zhuǎn)、編碼轉(zhuǎn)換、進(jìn)制轉(zhuǎn)換及運(yùn)算等常用技巧,需要的朋友可以參考下2016-07-07
python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5中QMainWindow, QWidget以及QDialog的區(qū)別和選擇
這篇文章主要介紹了python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5中QMainWindow, QWidget以及QDialog的區(qū)別和選擇,需要的朋友可以參考下2020-02-02
Python FastAPI 多參數(shù)傳遞的示例詳解
這篇文章主要介紹了Python FastAPI 多參數(shù)傳遞,FastAPI通過(guò)模板來(lái)匹配URL中的參數(shù)列表,大概分為三類方式傳遞參數(shù),每種方式結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12

