關于Kotlin的自動類型轉換詳解
前言
Kotlin 1.4 正式版在好早以前就已經發(fā)布了。關于那些“看得見”的新特性,比如SAM轉換、參數列表末尾的可選逗號什么的,已經有無數文章介紹過了。所以本文打算介紹一些可能是鮮為人知的、Kotlin 官方團隊偷偷塞進 1.4 的新特性。
不過單獨講這些東西會顯得文章太過單薄,于是我打算把其他相似的東西拉一起湊湊字數。
本文使用的 Kotlin 版本為 Kotlin 1.4。
本文要講的東西
看題目就知道了,Kotlin 里自動類型轉換(automatic type conversion)。這里講的不是 「把一個 String 轉成 Any,再轉成 String」 這種和子類型有關的東西,當然也不是 Smart Cast,而是兩個不相容的類型之間的轉換,比如說 Int 轉成 Long,如下文所示。
數值轉換
一般地,在 Kotlin 里我們不能像 Java 一樣直接把一個 Int 類型的東西賦值給 Long 類型的變量,因為它們之間并不具有子類型關系。像下面這樣會得到一個編譯錯誤:
val int: Int = 555 val long: Long = int // 編譯錯誤! println(long)
你需要調用標準庫提供給你的那些 toXXX 函數把數值轉換成其他類型的數值。
val int: Int = 555 val long: Long = int.toLong() // OK println(long)
Kotlin 官方團隊曾經表示過不喜歡隱式(implicit)的東西,關于數值的隱式類型轉換也包括在內。這就導致了使用 Kotlin 在進行一些關于數值方面的操作時,有時候會寫出一些看起來無比蛋疼的代碼。
Bennyhuo:就是有時候寫點兒計算比較多的代碼,滿篇的 toFloat toDouble。
不一般地,我們可以使用 @Suppress 來搞事:
val int: Int = 233 @Suppress("TYPE_MISMATCH") val long: Long = int println(long) // 233
這個代碼是可以跑起來的,而且你真的可以從字節(jié)碼里看到那個把 Int 轉成 Long 的指令 I2L。
不過我不確定 Kotlin 的其他 target 是否能這樣用,我也不保證這樣寫完全不會出問題。(這里是關于 @Suppress 的免責聲明,請讀者自行腦補)
SAM Conversion
SAM 轉換也是一種自動類型轉換。它把一個 lambda 表達式(具有函數類型)轉成某個具體的接口類型。
fun interface ISome { fun some() } fun useSome(some: ISome) {} useSome { println("some") }
在我的另一篇文章里有更詳細的介紹。
如果讀者不同意這個說法,可以選擇跳過本小節(jié)內容。
Coercion to Unit
我們都知道 Kotlin 的 lambda 表達式是使用里面最后一個表達式的值來作為 lambda 的返回值的。比如這樣:
val block = { "yeah" }
block 的類型是 () -> String。
然后我們來看看這樣的情況:
fun test(block: () -> Unit) { println(block()) } test { "yeah" } // 輸出 Unit
相信很多人都熟悉這樣的寫法。
在某些初學者的眼里這看起來像是把一個 () -> String 類型的 lambda 傳給了需要 () -> Unit 類型的函數。
這就是 coercion to unit,一個很久以前就存在的特性,可以理解為編譯器自動幫你在 lambda 表達式的最后加了一行 Unit,把本來應該是() -> String 類型的 lambda 變成了 () -> Unit 類型。
在 Kotlin 1.4 版本,這個特性得到了進化,你甚至可以這樣寫:
fun test(block: () -> Unit) { println(block()) } fun some(): String { return "str" } // 需要 Kotlin 1.4 版本 test(::some) // 輸出 Unit
編譯器幫你把 () -> String 類型的函數引用轉成了 () -> Unit。
Unit Conversion
警告:這是一項未完成的特性!
添加編譯器參數 -XXLanguage:+UnitConversion,你就開啟了一個 Kotlin 官方團隊偷偷塞進 1.4 版本的未完成的新特性。
這個特性允許你寫出這樣的代碼:
fun test(block: () -> Unit) { println(block()) } fun some(block: () -> String) { test(block) // 這里是重點 // 如果你不加那個編譯器參數,會報錯 } fun main() { some { "str" } // 理論上會輸出 Unit }
在函數 some 里把一個 () -> String 傳給了 test 函數,可以看出來這個特性其實和 coercion to unit 是差不多的。
理論上這樣的代碼運行時會輸出 Unit,但是目前由于該特性的代碼生成沒寫好,得不到預期的結果。
另外,在開啟了這個特性后,() -> String 并不會成為 () -> Unit 的子類型,它們依然是兩個不相容的類型。
Suspend Conversion
警告:這是一項未完成的特性!
這是本文要介紹的第二個 Kt 官方團隊偷偷塞進 1.4 版本的未完成的新特性。
比如說我們有這樣的一個函數:
fun test(f: suspend () -> Unit) { // do something with f }
我們可以這樣調用它:
test { println("hi") }
但是這樣不行:
val f = { println("hi") } test(f) // 編譯錯誤
編譯器會告訴你類型不匹配,f 是 () -> Unit 類型,test 函數需要 suspend () -> Unit 類型的參數。
當你添加了編譯器參數 -XXLanguage:+SuspendConversion,就可以讓上面的代碼通過編譯。
也就是說這個特性可以幫你把普通函數類型的值轉成 suspend 函數類型。
當然由于這是未完成的功能,即使可以通過編譯,但是跑起來還是會炸。
這個特性或許會在 Kotlin 1.5 版本完工,但請不要抱有期待。
結尾
我并不想討論「為什么要加這種奇怪的特性」之類的話題。
不可否認的是,在有限的程序員生涯中,這些新特性可能一次也用不上。上面提到的問題也都有相應的 workaround,不需要新特性也可以寫出等價的代碼,就是沒有那么優(yōu)雅罷了(
到此這篇關于關于Kotlin自動類型轉換的文章就介紹到這了,更多相關Kotlin自動類型轉換內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Ubuntu中為Android系統(tǒng)實現內置Java應用程序測試Application Frameworks層的硬件服務
本文主要介紹Ubuntu中為Android系統(tǒng)內置應用訪問Application Frameworks層的硬件服務,這里提供了詳細的流程和代碼實例,有興趣的朋友可以參考下2016-08-08Android編程實現音量按鈕添加監(jiān)聽事件的方法
這篇文章主要介紹了Android編程實現音量按鈕添加監(jiān)聽事件的方法,結合實例形式分析了Android事件監(jiān)聽實現音量控制的相關操作技巧,需要的朋友可以參考下2017-06-06