翻译说明: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,欢迎加入咱们~~~