Android第一行代码——快速入门 Kotlin 编程(10.6 Kotlin课堂:泛型的高级特性)

目录

10.6 Kotlin课堂:泛型的高级特性        

10.6.1        对泛型进行实化


10.6 Kotlin课堂:泛型的高级特性        

        还记得在第 8 章的 Kotlin 课堂里我们学习的 Kotlin 泛型的基本用法吗?这些基本用法其实和Java 中泛型的用法是大致相同的,因此也相对比较好理解。然而实际上,Kotlin 在泛型方面还提供了不少特有的功能,掌握了这些功能,你将可以更好玩转 Kotlin ,同时还能实现一些不可思议的语法特性,那么我们自然不能错过这部分内容了。

10.6.1        对泛型进行实化

        泛型实化这个功能对于绝大多数 Java 程序员来讲是非常陌生的,因为 Java 中完全没有这个概 念。而如果我们想要深刻地理解泛型实化,就要先解释一下 Java 的泛型擦除机制才行。

        在JDK 1.5之前,Java 是没有泛型功能的,那个时候诸如 List 之类的数据结构可以存储任意类型的数据,取出数据的时候也需要手动向下转型才行,这不仅麻烦,而且很危险。比如说我们在 同一个 List 中存储了字符串和整型这两种数据,但是在取出数据的时候却无法区分具体的数据类 型,如果手动将它们强制转成同一种类型,那么就会抛出类型转换异常。

        于是在JDK 1.5中,Java 终于引入了泛型功能。这不仅让诸如 List 之类的数据结构变得简单好 用,也让我们的代码变得更加安全。

        但是实际上,Java 的泛型功能是通过类型擦除机制来实现的。什么意思呢?就是说泛型对于类 型的约束只在编译时期存在,运行的时候仍然会按照 JDK 1.5之前的机制来运行,JVM是识别不 出来我们在代码中指定的泛型类型的。例如,假设我们创建了一个 List<String> 集合,虽然 在编译时期只能向集合中添加字符串类型的元素,但是在运行时期JVM并不能知道它本来只打算 包含哪种类型的元素,只能识别出来它是个List。

        所有基于 JVM 的语言,它们的泛型功能都是通过类型擦除机制来实现的,其中当然也包括了 Kotlin 。这种机制使得我们不可能使用 a is T 或者 T::class.java 这样的语法,因为 T 的实际 类型在运行的时候已经被擦除了。

        然而不同的是,Kotlin 提供了一个内联函数的概念,我们在第 6 章的 Kotlin 课堂中已经学过了这个知识点。内联函数中的代码会在编译的时候自动被替换到调用它的地方,这样的话也就不存 在什么泛型擦除的问题了,因为代码在编译之后会直接使用实际的类型来替代内联函数中的泛 型声明,其工作原理如 图10.15 所示。

图10.15        内联函数的代码替换过程

        最终代码会被替换成如 图10.16 所示的样子。

图10.16        替换完成后的代码

        可以看到,bar() 是一个带有泛型类型的内联函数,foo() 函数调用了 bar() 函数,在代码编 译之后,bar() 函数中的代码将可以获得泛型的实际类型。

        这就意味着,Kotlin 中是可以将内联函数中的泛型进行实化的。

        那么具体该怎么写才能将泛型实化呢?首先,该函数必须是内联函数才行,也就是要用 inline 关键字来修饰该函数。其次,在声明泛型的地方必须加上 reified 关键字来表示该泛型要进行 实化。示例代码如下:

inline fun <reified T> getGenericType() { 
} 

        上述函数中的泛型 T 就是一个被实化的泛型,因为它满足了内联函数和 reified 关键字这两个前 提条件。那么借助泛型实化,到底可以实现什么样的效果呢?从函数名就可以看出来了,这里 我们准备实现一个获取泛型实际类型的功能,代码如下所示:

inline fun <reified T> getGenericType() = T::class.java 

        虽然只有一行代码,但是这里却实现了一个 Java 中完全不可能实现的功能: getGenericType() 函数直接返回了当前指定泛型的实际类型。T.class 这样的语法在Java 中是不合法的,而在 Kotlin 中,借助泛型实化功能就可以使用 T::class.java 这样的语法了。

        现在我们可以使用如下代码对 getGenericType() 函数进行测试:

fun main() { 
    val result1 = getGenericType<String>() 
    val result2 = getGenericType<Int>() 
    println("result1 is $result1") 
    println("result2 is $result2") 
} 

        这里给 getGenericType() 函数指定了两种不同的泛型,由于 getGenericType() 函数会将 指定泛型的具体类型返回,因此这里我们将返回的结果进行打印。

        现在运行一下 main() 函数,结果如图10.17 所示。

图10.17        泛型时化功能的运行结果

        可以看到,如果将泛型指定成了 String,那么就可以得到 java.lang.String 的类型;如果 将泛型指定了 Int,就可以得到 java.lang.Integer 的类型。

        关于泛型实化的基本用法就介绍到这里,接下来我们看一看,泛型实化在 Android 项目当中具体 可以有哪些应用。

  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值