val b=a?:0,a是Double 類型,那 b 是什麼類型?
前面有朋友看了我的文章之後,表示都不敢用 Kotlin 了。
這事兒要辯證的看待,Java 坑那麼多,你還不是照樣用么?
而 Kotlin 本身坑很少,大多數都是為了照顧 Java 而出現的一些有意思的問題,對於這些問題的深挖可以讓我們看得更多,想得更深,了解得更多,然則與坑斗,其樂無窮也。
本文基於 Kotlin 1.1.4。
1. 數值類型的推導
我們的標題其實已經說得很清楚了,我把完整的代碼貼出來:
問題就是,請問 的類型。
這個問題看上去似乎並沒有什麼難度,在 Kotlin 當中,所有數值類型都是 的子類,也就是說 和 都是它的子類,這種情況下, 的類型應該毫無疑問的是 。
真的是這樣嗎?
很遺憾,IntelliJ 告訴我們, 的類型是 。
注意,這裡是變數 的類型推導, 指向的內存的類型取決於真實的內存數據。
為什麼會這樣?難道我發現了一個編譯器的 Bug?
2. 普通類繼承的推導
有了這個發現,我倒要試試看,是不是所有類的推導都會直接推導為 。
先聲明下面的類型:
看下我們的測試代碼:
有了前面的經驗,我就有點兒擔心 Kotlin 會把 這個變數推導成 了,不過結果卻並不是這樣:
推導的類型是 ,是合乎情理的。
3. 位元組碼分析
面對這個類型的結果差異,我瞬間想到了看看位元組碼,
對應的位元組碼:
注意,此處為了阻止編譯器優化位元組碼,我們需要對變數 有操作,例如在後面添加 ,否則位元組碼可能與文中有出入
而:
對應的位元組碼:
為啥前面就沒有 呢?位元組碼是生成的結果,不是類型推導的原因,通過這個結果我們只能推測到類型推導的結果在第一個那裡就被推導為 了。
當然,如果你願意,你也可以明確指定 的類型:
這時候位元組碼也會變成:
儘管這樣會達到我們的目的,但並不能解釋前面我們遇到的問題。
4. 幾個猜想
最近在看《柔軟的宇宙》,科學家們在發現問題的時候總是先來個猜想,然後想辦法通過實踐來證明。前面被數值的基本類型的映射坑了太多把了,所以我想一定是因為後面的那個 被識別成了 Java 基本類型的 。
那麼我們想辦法把這個這個 變成裝箱類型會怎麼樣呢?
結果, 仍然是 。換句話說, 的類型推導實際上與 Java 的基本類型沒有任何關係。
難道只是 的問題? 這時候我突然想到前面剛剛被坑過的 ,試了一下:
結果再一次打臉,這次 的類型居然就是 了。
想來想去,這可能就是 Kotlin 編譯器在求兩個類型的公共父類的時候有些奇怪的東西我沒有 GET 到,那這個奇怪的東西究竟是什麼呢?
5. Google 不到的東西,只有源碼會告訴我
吃螃蟹,就得做好為別人栽樹的思想準備。像 Kotlin 這樣的新語言,很多時候 Google 也不會告訴我們答案,這也是很多人望而生卻的原因。
為了搞清楚編譯器是怎麼做的,我們需要把Kotlin(https://github.com/JetBrains/kotlin)的源碼拖下來,編譯運行,打斷點調試,找到一個叫做 的類,這個類實際上就是負責計算公共父類的,有興趣的朋友也可以自行研讀一下它的 方法,我們在這裡只簡單介紹一下公共父類的計算方法:
和 除了有個公共父類 之外,還都實現了 介面,所以在計算公共父類的時候,先把他們都羅列出來,然後最終變成了求 和 的公共父類,那麼自然就是 了。
而我們再來看看另外的情形:
和 只有一個公共父類 ,不像前面還有個公共父介面 ,這樣問題就簡單了,直接把 的類型推導成 而不是 。
那麼對於我們自定義的那一組例子,結果也類似:
不過我們稍加修改,結果就又是一番情景了:
這下你能想明白是為啥了吧?
同樣的,在 YouTrack 上面還有這樣的一個 Issue,Common super type for different enum items is Any instead of common declared super type(https://youtrack.jetbrains.com/issue/KT-4687),原因也是類似的。
6. 再問個為什麼
這裡有人肯定還是覺得奇怪,因為 和 的父類和介面都一樣呀,為啥推導的結果不是 呢?
顯然這裡 Kotlin 的開發者也是很糾結的,既然可以推導成 ,那麼推導成 可以不可以呢?換句話說,對於兩個類型有兩個以上沒有繼承關係的公共父類(介面)的情形,推導的結果會有歧義,可能也是為了消除這種歧義,Kotlin 編譯器採用了一種比較穩妥的方式來處理,不偏袒任何一方,直接將推導的結果定為 也是合情合理的。
這時候如果你明確知道自己想要什麼,例如前面的例子,我們想要 的類型是 而不是 ,那麼只需要顯式的為 聲明類型就可以了。
7. 看看其他語言怎麼做
對於類似的情形,C# 直接報錯:
即便 和 有公共父類, C# 仍然需要你明確他們的類型,大家可以參考 StackOverflow 上面的討論:No implicit conversion when using conditional operator(https://stackoverflow.com/questions/6137974/no-implicit-conversion-when-using-conditional-operator)
當然,如果能像 Scala 那樣推導,也是不錯滴:
但不是所有的類都有 Scala 的交集類型(intersection type )。
TAG:Kotlin |
※python中list,array,mat,tuple大小及類型
※Facebook主頁成效分析:帖子有什麼作用?Facebook帖子類型&作用
※python基礎之變數類型number(math模塊)
※Shopify賬號有哪些類型?Shopify賬號類型&賬號許可權
※Hibernate 映射枚舉Enum 類型的屬性
※「Good idea/solution」 類型作文審題
※Chrome 66 新特性:CSS 類型對象模型,非同步剪貼板 API,AudioWorklet,等
※蔣欣和angelababy穿同一類型裙子,為何差距那麼大?快get起來
※類型轉換運算符 運算符重載 operator new operator delete
※TypeScript基礎之高級類型的可辨識聯合(Discriminated Unions)
※「Python」Chapter1 變數和簡單數據類型
※Google出品的Python代碼靜態類型分析器:Pytype
※SM、jyp、yg、cube、ymc等公司類型——其中big hit最霸氣
※Nike VaporMax最新諜照釋出,2019年的版本會是你喜歡的類型么?
※solidity之地址類型
※Shoes Lab跑鞋風雲榜第三期:亞瑟士Kayano系列即將上市的25,適合什麼類型的跑者?
※意甲寶貝Valeria Orsini度假,網友:身材容貌美,我喜歡的類型
※7種類型的Logo設計:哪種Logo適合您?
※TheShy未來可比肩Faker?兩個人是同類型選手
※Angelababy宣布發專輯,並詢問粉絲喜歡聽什麼類型的歌