相比于Java中的泛型用法,Kotlin中的泛型还提供很多特有的功能,其中就包括泛型实化
类型擦除机制
在JDK 1.5之前Java是没有泛型的,从JDK 1.5开始Java加入了泛型功能。Java中泛型功能是通过类型擦除机制来实现的。
类型擦除的意思是,泛型对于类型的约束只在编译时期存在,运行的时候依旧按照JDK 1.5之前的机制来运行,JVM是识别不到我们在代码中指定的泛型类型的。
这就是说,假如我们创建了一个List<String>
集合,虽然在编译时期我们只能往这个集合中添加String
类型的元素,但是在运行时JVM只能识别出来它是个List
,并不能知道它原本是只打算包含String
类型的元素的。
所有基于JVM的语言的泛型功能都是通过类型擦除机制来实现的,所以Kotlin也是。这种机制使我们不可能使用a is T
或T::class.java
这样的语法,因为T的实际类型在运行时已经被擦除了
泛型实化
但是,Kotlin提供了一个内联函数的概念:内联函数中的代码在编译时会被替换到调用它的地方。
最终代码会被替换成这样:
1 | fun foo() { |
这样就不存在类型擦除的问题了,因为代码在编译之后会直接使用实际的类型来替代内联函数中的泛型声明。
上图中的bar()
是一个带有泛型类型的内联函数,foo()
函数调用了bar()
函数,在代码编译之后,bar()
函数中的代码将可以获得泛型的实际类型
所以,Kotlin中是可以将内联函数中的泛型进行实化的
要想将泛型实化,首先需要用inline
关键字来修饰该函数,然后在声明泛型的地方加上reified
关键字来表示该泛型要进行实化
1 | inline fun <reified T> getGenericType() { |
借助泛型实化,我们可以实现获取泛型实际类型的功能:
1 | inline fun <reified T> getGenericType() = T::class.java |
虽然只有一行代码,但是这里却实现了一个Java中完全不可能实现的功能:getGenericType()
函数直接返回了当前指定泛型的实际类型。T.class
这样的语法在Java中是不合法的,而在Kotlin中,借助泛型实化功能就能使用T::class.java
这样的语法了
下面对这个功能进行测试:
1 | fun main() { |
运行结果
泛型实化的应用
泛型实化功能允许我们在泛型函数当中获得泛型的实际类型,这使得类似a is T
、T::class.java
这样的语法变得合法。
在Android开发中,启动一个Activity的代码经常这样写:
1 | val intent = Intent(context, NewActivity::class.java) |
但是在Kotlin中,我们有更好的选择,先编写一个startActivity()
函数:
1 | inline fun <reified T> startActivity(context : Context) { |
现在,我们启动Activity时只需要这样调用startActivity()
:
1 | startActivity<NewActivity>(context) |