Kotlin泛型简介

泛型,这个概念在很多编程语言里面都存在。在中大型软件开发当中,我们对泛型的使用也十分频繁,因为它可以让我们在不同类型之间复用相似的逻辑代码。

不管是 Android 领域,还是后端领域,泛型在软件的架构当中都有着举足轻重的地位。只有透彻理解了泛型,我们才能理解各种设计模式,进而才可能设计出合理的软件架构。使用泛型的好处就在于,我们可以复用程序代码的逻辑,借助这个特性,我们可以在程序的基础上再做一次抽象。

我们在定义泛型的时候,其实还可以为它的泛型参数增加一些边界限制。

型变(Variance)

简单来说,它就是为了解决泛型的不变性问题。事实上,型变讨论的是:在已知 Cat 是 Animal 的子类的情况下,MutableList与MutableList之间是什么关系。

在正常情况下,编译器会认为它们两者是没有任何关系的。换句话,也就是说,泛型是不变的。

在默认情况下,编译器会认为MutableList与MutableList之间不存在任何继承关系,它们也无法互相替代,这样就不会出现前面提到的两种问题。这就是泛型的不变性。

逆变(Contravariant)

这种父子关系颠倒的现象,我们就叫做“泛型的逆变”。上面这两种修改方式,就分别叫做使用处逆变和声明处逆变。

而除了父子关系颠倒的现象,泛型当中还存在一种父子关系一致的现象,也就是泛型的协变。

协变(Covariant)

上面两种修改的方式,就分别叫做使用处协变和声明处协变。

Kotlin泛型简介

星投影(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

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!