翻譯說明:html
原標題: Getting Real with Kotlin's Reified Type Parametersjava
原文地址: typealias.com/guides/gett…json
原文做者: Dave Leedsoracle
翻譯系列:app
原創系列:ide
實戰系列:函數
本篇已是Kotlin泛型系列第三篇了,先來回顧下前面兩篇分別講的是泛型中的類型形參和類型實參以及何時該使用類型形參約束。今天咱們來講點Kotlin獨有的泛型特性,可是Java中是沒有的。那就是Kotlin中reified關鍵字修飾的泛型實化類型參數。再說這個以前我以爲有必要科普一下歷史背景以及爲何Kotlin會出現實化類型參數。post
歷史背景:ui
咱們都知道Java中的泛型是在JDK1.5的版本引入的,但是集合Collection在JDK1.2版本中就引入的,咱們如今所看到的List<T>
,是在泛型出來後加入的,那麼JDK1.2以前就直接用List
(java中俗稱原生態類型)表示。問題來了爲了兼容以前的版本Java採用所謂的僞泛型,僞泛型有個什麼特徵我想你們應該猜到那就是泛型擦除,就是泛型類型信息在編譯期都會被抹掉,無論你是List<String>
仍是List<Float>
在運行時他們都同樣,那都是List類型,泛型類型信息已經被擦除了。固然泛型擦除也有它的好處,固然這不是此次討論的重點。僞泛型對應的就是真泛型,若是熟悉C#就知道,它是真泛型,不會存在類型擦除狀況,具體能夠本身去了解下。this
時勢造英雄Kotlin登場:
顯然咱們知道,泛型擦除在一些開發場景下是有很大影響,使用起來很是不便,Kotlin這門新的語言不像Java同樣有太多的歷史負擔,它就像是全局者同樣,看着Java以前的坑走過來的,因而乎它想來填一填。有的人可能會問了那它是否是像C#那樣採用真泛型,答案不是。咱們都知道Kotlin力求作到與Java百分百的互操做性,因此Kotlin妥協了仍是採用僞泛型,因此它和Java同樣依然會存在泛型擦除問題。可是很幸運地是Kotlin偷偷給咱們開了一個後門那就是今天的主角Reified實化類型參數,它能夠保證運行時依然能拿到泛型具體實際類型。Reified實化類型參數已經大量運用在Kotlin的anko中,關於這塊後面博客會細講。
讓咱們來想想在Kotlin中你能夠類名來作些什麼-想一想你在源碼中編寫類名的全部場景。我想到了列舉如下15種情景,沒想全可能會有漏一些。讓咱們一塊兒來看下吧...
private val thing: Thing
複製代碼
fun doSomething(thing: Thing) {}
複製代碼
val list = listOf<Thing>()
複製代碼
class Item<T : Thing>
複製代碼
something as Thing
複製代碼
open class Thing {}
複製代碼
class Other : Thing()
複製代碼
import com.example.Thing
複製代碼
typealias Thingy = Thing
複製代碼
val thing = Thing()
複製代碼
catch (e: ExceptionalThing)
複製代碼
Thing.doStaticStuff()
複製代碼
val function = Thing::doSomething
複製代碼
something is Thing
複製代碼
val clazz = Thing::class.java
複製代碼
如今,最大的問題是:
在上面哪些案例中咱們應該使用泛型類型參數引用而不是真實的類名?
換句話說,上面15種案例中哪些咱們能夠用類型參數好比 T
去替代Thing
類呢?
你想到了什麼? 這是我能想到的 - 上面的案例1-5均可以採用類型參數。讓咱們一塊兒展現全部五個:
class GenericThing<T>(constructorArg: T) {
// 1. Define a member property 定義一個成員屬性
private val thing: T = constructorArg
// 2. Define the type of a function argument 定義函數參數
fun doSomething(thing: T) = println(thing)
// 3. Use as a type argument 定義泛型類型實參
fun emptyList() = listOf<T>()
// 4. Use as a type parameter constraint, and... 使用做爲類型形參約束
// 5. Cast to the type (produces "unchecked cast" warning) 強制類型轉換
fun <U : T> castIt(): T = thing as U
}
複製代碼
引用類型參數適用於案例1-5。可是對於案例6-15,若是咱們要在(例如,Thing或ExceptionalThing)的地方替換類型參數(例如T),則最終會出現編譯器錯誤。
Java限制了哪些類型是reifiable - reifiable也就意味着它們「在運行時徹底可用」(具體可查閱:Java SE specs on reifiable types),泛型類型參數一般在編譯期間被擦除,可是在Kotlin中的reified類型參數的狀況下, 因爲底層語法下一些巧妙的技巧,讓運行時也能準確拿到泛型參數類型信息。
Reified類型參數僅適用於函數(或具備get()函數的擴展屬性),而且僅適用於聲明爲inline內聯的函數。這是一個例子:
inline fun <reified T> Any.isInstanceOf(): Boolean = this is T
複製代碼
當您將函數標記爲inline時,編譯器會把實現內聯函數的字節碼插入到每次調用發生的地方。這就是reified類型的工做原理 - 具體實際類型在調用地方是已經知道的,所以在調用x.isInstanceOf<String>()
有效地把x編譯爲String.
上面的案例15是許多Kotlin開發人員最喜歡的案例。假設咱們有一個User類,以及咱們想要讀取的JSON字符串
data class User(val first: String, val last: String)
val json = """{ "first": "Sherlock", "last": "Holmes" }"" 複製代碼
在Java序列化庫(如Gson)中,當您想要反序列化該JSON字符串時,您最終必須將Class對象做爲參數傳遞,以便Gson知道您想要的類型。
User user = new Gson().fromJson(getJson(), User.class);
複製代碼
如今,讓咱們一塊兒展現reified類型實化參數的魔法 咱們將建立一個很是輕量級的擴展函數來包裝Gson方法:
inline fun <reified T> Gson.fromJson(json: String) =
fromJson(json, T::class.java)
複製代碼
如今,在咱們的Kotlin代碼中,咱們能夠反序列化JSON字符串,甚至根本不須要傳遞類型信息!
val user: User = Gson().fromJson(json)
複製代碼
Kotlin根據它的用法推斷出類型 - 由於咱們將它分配給User
類型的變量,Kotlin使用它做爲fromJson()
的類型參數。或者,您可使類型推斷其餘的類:
val user = Gson().fromJson<User>(json)
複製代碼
在這種狀況下,從傳遞給fromJson()
的類型參數推斷出user
類型。
若是想了解更多關於Reified Type Parameters.請關注[譯]Kotlin的獨門祕籍Reified實化類型參數(下篇),未翻譯,敬請期待。
這篇文章屬於reified-type-parameter實化類型參數的上篇,算是一個簡單開頭介紹怎麼使用reified,而後前面花了一些篇幅闡述了開發中什麼狀況下可使用泛型,感受這點應該更實用吧,下篇譯文將會比較深刻reified講解實化類型參數,敬請期待。
歡迎關注Kotlin開發者聯盟,這裏有最新Kotlin技術文章,每週會不按期翻譯一篇Kotlin國外技術文章。若是你也喜歡Kotlin,歡迎加入咱們~~~