泛型,这个概念在很多编程语言里面都存在。在中大型软件开发当中,我们对泛型的使用也十分频繁,因为它可以让我们在不同类型之间复用相似的逻辑代码。
不管是 Android 领域,还是后端领域,泛型在软件的架构当中都有着举足轻重的地位。只有透彻理解了泛型,我们才能理解各种设计模式,进而才可能设计出合理的软件架构。使用泛型的好处就在于,我们可以复用程序代码的逻辑,借助这个特性,我们可以在程序的基础上再做一次抽象。
我们在定义泛型的时候,其实还可以为它的泛型参数增加一些边界限制。
型变(Variance)
简单来说,它就是为了解决泛型的不变性问题。事实上,型变讨论的是:在已知 Cat 是 Animal 的子类的情况下,MutableList与MutableList之间是什么关系。
在正常情况下,编译器会认为它们两者是没有任何关系的。换句话,也就是说,泛型是不变的。
在默认情况下,编译器会认为MutableList与MutableList之间不存在任何继承关系,它们也无法互相替代,这样就不会出现前面提到的两种问题。这就是泛型的不变性。
逆变(Contravariant)
这种父子关系颠倒的现象,我们就叫做“泛型的逆变”。上面这两种修改方式,就分别叫做使用处逆变和声明处逆变。
而除了父子关系颠倒的现象,泛型当中还存在一种父子关系一致的现象,也就是泛型的协变。
协变(Covariant)
上面两种修改的方式,就分别叫做使用处协变和声明处协变。
星投影(Star-Projections)
Kotlin 当中还有一个概念叫做“星投影”。虽然你听起来可能会觉得这个词很吓人,但其实它的概念很简单。所谓的星投影,其实就是用“星号”作为泛型的实参。
那么,什么情况下,我们需要用星号作为泛型实参呢?答案其实也很简单,当我们不关心实参到底是什么的时候。
// 区别在这里
// ↓
class Restaurant<out T: Food> {
fun orderFood(): T {}
}
fun findRestaurant(): Restaurant<*> {}
fun main() {
val restaurant = findRestaurant()
// 注意这里
// ↓
val food: Food = restaurant.orderFood() // 返回值是:Food或其子类
}
从这个例子我们能看到,当我们为 Restaurant 泛型类型增加了上界 Food 以后,即使我们使用了“星投影”,也仍然可以通过调用 restaurant.orderFood(),来拿到 Food 类型的变量。
在这里,food 的实际类型肯定是 Food 或者是 Food 的子类,因此我们可以将其看作是 Food 类型。
什么时候用逆变,什么时候用协变
// 逆变
// ↓
class Controller<in T> {
// ①
// ↓
fun turnOn(tv: T)
}
// 协变
// ↓
class Restaurant<out T> {
// ②
// ↓
fun orderFood(): T { /*..*/ }
}
-
对于逆变的情况,我们模拟的是买遥控器的场景。请注意注释①的地方,我们的泛型 T,它最终会以函数的参数的形式,被传入函数的里面,这往往是一种写入行为,这时候,我们使用关键字 in。 -
对于协变的情况,我们模拟的是点外卖的场景。请注意注释②的地方,我们的泛型 T,它最终会以返回值的形式,被传出函数的外面,这往往是一种读取行为,这时候,我们使用关键字 out。
所以,如果要以更加通俗的语言来解释逆变与协变的使用场景的话,我们可以将其总结为:传入 in,传出 out。或者,我们也可以说:泛型作为参数的时候,用 in,泛型作为返回值的时候,用 out。
总结
-
泛型,是对程序的一种抽象。通过泛型,我们可以实现代码逻辑复用的目的,Kotlin 标准库当中很多源代码也都是借助泛型来实现的。 -
从型变的位置来分类的话,分为使用处型变和声明处型变。 -
从型变的父子关系来分类的话,分为逆变和协变。逆变表示父子关系颠倒了,而协变表示父子关系和原来一致。 -
型变的口诀:泛型作为参数,用 in;泛型作为返回值,用 out。在特殊场景下,同时作为参数和返回值的泛型参数,我们可以用 @UnsafeVariance 来解决型变冲突。 -
星投影,就是当我们对泛型的具体类型不感兴趣的时候,直接传入一个“星号”作为泛型的实参。
— End —
原文始发于微信公众号(君伟说):Kotlin泛型简介
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/115152.html