Kotlin 的 in 和 out 说起,来聊一聊泛型

2023年 7月 12日 52.6k 0

Kotlin 的 in 和 out 说起

这期是码上开学 Kotlin 系列的独立技术点部分的第一期,我们来聊一聊泛型。

提到 Kotlin 的泛型,通常离不开 in 和 out 关键字,但泛型这门武功需要些基本功才能修炼,否则容易走火入魔,待笔者慢慢道来。

下面这段 Java 代码在日常开发中应该很常见了:

☕️
List textViews = new ArrayList();

其中 List 表示这是一个泛型类型为 TextView 的 List

那到底什么是泛型呢?我们先来讲讲泛型的由来。

现在的程序开发大都是面向对象的,平时会用到各种类型的对象,一组对象通常需要用集合来存储它们,因而就有了一些集合类,比如 ListMap 等。

这些集合类里面都是装的具体类型的对象,如果每个类型都去实现诸如 TextViewListActivityList 这样的具体的类型,显然是不可能的。

因此就诞生了「泛型」,它的意思是把具体的类型泛化,编码的时候用符号来指代类型,在使用的时候,再确定它的类型。

前面那个例子,List 就是泛型类型声明。

既然泛型是跟类型相关的,那么是不是也能适用类型的多态呢?

先看一个常见的使用场景:

☕️
TextView textView = new Button(context);
// ? 这是多态

List buttons = new ArrayList();
List textViews = buttons;
// ? 多态用在这里会报错 incompatible types: List cannot be converted to List

我们知道 Button 是继承自 TextView 的,根据 Java 多态的特性,第一处赋值是正确的。

但是到了 List 的时候 IDE 就报错了,这是因为 Java 的泛型本身具有「不可变性 Invariance」,Java 里面认为 List 和 List 类型并不一致,也就是说,子类的泛型(List)不属于泛型(List)的子类。

Java 的泛型类型会在编译时发生类型擦除,为了保证类型安全,不允许这样赋值。至于什么是类型擦除,这里就不展开了。

你可以试一下,在 Java 里用数组做类似的事情,是不会报错的,这是因为数组并没有在编译时擦除类型:

☕️
TextView[] textViews = new TextView[10];

但是在实际使用中,我们的确会有这种类似的需求,需要实现上面这种赋值。

Java 提供了「泛型通配符」 ? extends 和 ? super 来解决这个问题。

Java 中的 ? extends

在 Java 里面是这么解决的:

☕️
List buttons = new ArrayList();
      ?
List 其实是 List list = buttons;
Object obj = (0);

(obj); // ? 这里还是会报错

和前面的例子一样,编译器没法确定 ? 的类型,所以这里就只能 get 到 Object 对象。

同时编译器为了保证类型安全,也不能向 List 中添加任何类型的对象,理由同上。

由于 add 的这个限制,使用了 ? extends 泛型通配符的 List,只能够向外提供数据被消费,从这个角度来讲,向外提供数据的一方称为「生产者 Producer」。对应的还有一个概念叫「消费者 Consumer」,对应 Java 里面另一个泛型通配符 ? super

Java 中的 ? super

先看一下它的写法:

☕️
?
List

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论