深入理解Kotlin的泛型系统

前言

Kotlin 的泛型与 Java 一样,都是一种语法糖,只在源代码里出现,编译时会进行简单的字符串替换。

泛型是静态类型语言中不可缺少的一部分,Kotlin 的泛型定义和使用都类似 Java,但也有一些基于工程实践考虑的优化和改进。

泛型(Generics)其实就是把类型参数化,真正的名字叫做 类型参数,它给强类型编程语言加入了更强的灵活性。在 Java 中,只要是有类型的 API 元素,都可以泛型化,也就是泛型类、泛型接口、泛型方法和泛型属性,泛型类和泛型接口可以统称为泛型类型。其中最重要的是泛型类型和泛型方法,Kotlin 泛型系统继承了 Java 泛型系统,同时添加了一些强化的地方。

实化泛型参数

在 Java 中经常会定义这种方法:

<T> void someFunction(Class<T> clazz) {
 //...
}

这是因为 Java 的泛型擦除机制无法使用形如 T.class 来获取泛型的真实类型对象。但是在调用者看来,泛型却是实实在在的固定类型,所以这里借助 Kotlin 的内联函数 inline 可以实化泛型参数,在 Kotlin 中只需要这样:

fun <T> someFunction() {
 val clazz = T::class.java
}

泛型的协变、逆变

在 Java 中,定义带泛型的参数时为了更好的匹配目标类型,有 ? extends Type 和 ? super Type 两种形式,以 List 接口中的定义为例:

boolean addAll(Collection<? extends E> c);
void sort(Comparator<? super E> c);

addAll 方法中,Collection 中的泛型被定义成接收类型参数 E 的子类,这是因为需要读取也就 c 的值,所以需要保证 c 是 Collection 的子类;而 sort 方法中,则是需要类中的类型参数 E 能够被 Comparator 中的方法传入,所以也就需要保证 E 是 Comparator 类型参数的子类。

而 Kotlin 中,针对于这两种情况给了另外两个关键字:需要读取带泛型对象的值时,使用 out 来标记类型参数;需要传入类型参数的类型作为形参时,使用 in。

这两种关键字的命名的方向是不同的:Java 偏向于从原理的方向命名,而 Kotlin 的命名对于具体的使用场景更为直观。在 Kotlin 中,被 out 标记类型参数的类型称之为协变类型,它代表当 A 是 B 的子类时,C 也能作为 C 的子类使用;而被 in 标记类型参数的类型则相反,它代表当 A 是 B的子类时,C 是 C 的子类

从方法参数的使用上来说,Kotlin 和 Java 似乎没有什么不同,而不同的地方在于 Kotlin 可以将这种定义作用在类型定义上,官方称之为声明点变型;与之相对应的,像 Java 这种在方法参数上定义的被称为使用点变型。

声明点变型在类型的声明时定义了该类型参数是用在入参还是出参上,之后在这个类中所有用到的地方都会直接调用该类型的定义名称来使用该类型的协变或者逆变。而 Java 中需要在每次使用时来重复说明该处需要协变还是逆变。Kotlin 也可以进行使用点变型,只要和 Java 一样,在声明处不进行说明,而只在使用时声明就可以了。

「*」投影

因为 Kotlin 源码中不允许忽略泛型参数,所以在一些泛型不重要的地方,就不可避免的使用 来表示。当使用 时,为了保证类型安全,官方建议的模式是将泛型定义为 的对象封装起来,写操作一般是安全的,因为 可以接收一切类型;对于读操作可以进行安全的转换,对于不匹配的类型进行统一处理。

泛型的注意点:

在java编程中类型系统最棘手的一部分是通配符类型。但是,在Kotlin编程中,是没有通配符,采用声明变化和类型投影来替代。

通配符的作用: 使用界限通配符增加API的灵活性。

在Java编程中一个常见的问题:

List<String> strs = new ArrayList<String>();
List<Object> objs = strs; // !!! 这里会导致一个问题,在Java中是禁止这样做的
objs.add(1); //将integer类型的数据添加到String 类型的列表中
String s = strs.get(0); // !!! ClassCastException异常 : Integer类型不能转成String

在Kotlin编程中:

一旦声明类型后,不能加入其它的类型数据,如下图所示。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

您可能感兴趣的文章:

  • Kotlin 泛型详解及简单实例
  • Kotlin 基础教程之泛型
(0)

相关推荐

  • Kotlin 泛型详解及简单实例

     Kotlin 泛型详解 概述 一般类和函数,只能使用具体的类型:要么是基本类型,要么是自定义的类.如果要编写可以应用于多种类型的代码,这种刻板的约束对代码的限制很大.而OOP的多态采用了一种泛化的机制,在SE 5种,Java引用了泛型.泛型,即"参数化类型".一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参.那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用

  • Kotlin 基础教程之泛型

    Kotlin 支持泛型, 语法和 Java 类似. 例如,泛型类: class Hello<T>(val value: T) val box = Box<Int>(1) val box1 = Box(2) 泛型函数: fun <T> foo(item: T): List<T> { // do something } val list = foo<Int>(1) fun <T> T.toString2(): String { // 扩展

  • 深入理解Kotlin的泛型系统

    前言 Kotlin 的泛型与 Java 一样,都是一种语法糖,只在源代码里出现,编译时会进行简单的字符串替换. 泛型是静态类型语言中不可缺少的一部分,Kotlin 的泛型定义和使用都类似 Java,但也有一些基于工程实践考虑的优化和改进. 泛型(Generics)其实就是把类型参数化,真正的名字叫做 类型参数,它给强类型编程语言加入了更强的灵活性.在 Java 中,只要是有类型的 API 元素,都可以泛型化,也就是泛型类.泛型接口.泛型方法和泛型属性,泛型类和泛型接口可以统称为泛型类型.其中最重

  • 一篇文章弄懂Java和Kotlin的泛型难点

    Java 和 Kotlin 的泛型算作是一块挺大的知识难点了,涉及到很多很难理解的概念:泛型型参.泛型实参.类型参数.不变.型变.协变.逆变.内联等等.本篇文章就将 Java 和 Kotlin 结合着一起讲,按照我的个人理解来阐述泛型的各个知识难点,希望对你有所帮助

  • 图解 Kotlin SharedFlow 缓存系统及示例详解

    目录 前言 replay extraBufferCapacity onBufferOverflow SharedFlow Buffer 前言 Kotlin 为我们提供了两种创建“热流”的工具:StateFlow 和 SharedFlow.StateFlow 经常被用来替代 LiveData 充当架构组件使用,所以大家相对熟悉.其实 StateFlow 只是 SharedFlow 的一种特化形式,SharedFlow 的功能更强大.使用场景更多,这得益于其自带的缓存系统,本文用图解的方式,带大家更

  • 浅理解C++ 人脸识别系统的实现

    机器学习 机器学习的目的是把数据转换成信息. 机器学习通过从数据里提取规则或模式来把数据转成信息. 人脸识别 人脸识别通过级联分类器对特征的分级筛选来确定是否是人脸. 每个节点的正确识别率很高,但正确拒绝率很低. 任一节点判断没有人脸特征则结束运算,宣布不是人脸. 全部节点通过,则宣布是人脸. 工业上,常用人脸识别技术来识别物体. 基于深度学习的人脸识别系统,一共用到5个开源库:OpenCV(计算机视觉库).Caffe(深度学习库).Dlib(机器学习库).libfacedetection(人脸

  • Kotlin实现Android系统悬浮窗详解

    目录 Android 弹窗浅谈 系统悬浮窗具体实现 权限申请 代码设计 具体实现 FloatWindowService 类 FloatWindowManager 类 FloatWindowManager 类代码 FloatLayout 类及其 Layout HomeKeyObserverReceiver 类 FloatWindowUtils 类 总结 Android 弹窗浅谈 我们知道 Android 弹窗中,有一类弹窗会在应用之外也显示,这是因为他被申明成了系统弹窗,除此之外还有2类弹窗分别是

  • 初步理解Java的泛型特性

    在Java SE1.5中,增加了一个新的特性:泛型(日本语中的总称型).何谓泛型呢?通俗的说,就是泛泛的指定对象所操作的类型,而不像常规方式一样使用某种固定的类型去指定.泛型的本质就是将所操作的数据类型参数化,也就是说,该数据类型被指定为一个参数.这种参数类型可以使用在类.接口以及方法定义中.   一.为什么使用泛型呢?      在以往的J2SE中,没有泛型的情况下,通常是使用Object类型来进行多种类型数据的操作.这个时候操作最多的就是针对该Object进行数据的强制转换,而这种转换是基于

  • 深入理解C#实现快捷键(系统热键)响应的方法

    在应用中,我们可能会需要实现像Ctrl+C复制.Ctrl+V粘贴这样的快捷键,本文简单介绍了它的实现,并给出了一个实现类.(1)建立一个类文件,命名为HotKey.cs,代码如下: 复制代码 代码如下: using System;using System.Collections.Generic;using System.Runtime.InteropServices;using System.Windows.Forms;namespace KoalaStudio.BookshopManager{

  • Android在Kotlin中更好地使用LitePal

    Kotlin 是一个用于现代多平台应用的静态编程语言,由 JetBrains 开发. Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行. Kotlin已正式成为Android官方支持开发语言. 自从LitePal在2.0.0版本中全面支持了Kotlin之后,我也一直在思考如何让LitePal更好地融入和适配Kotlin语言,而不仅仅停留在简单的支持层面. Kotlin确实是一门非常出色的语言,里面有许多优秀的特性是在Java中无法实现的.因此,

随机推荐