Kotlin 中的协变与逆变

大师1 2024-04-30 ⋅ 22 阅读

在 Kotlin 中,我们经常听说协变和逆变这两个概念,它们涉及到了 Kotlin 泛型的特性。在本文中,我们将深入探讨协变和逆变的概念,以及它们在 Kotlin 中的应用。

泛型类型

首先,让我们回顾一下什么是泛型。泛型是一种参数化类型的机制,它可以让我们在定义类、接口或函数时,指定一种类型的占位符。通过使用泛型,我们可以创建通用的代码,以适应不同类型的数据。

在 Kotlin 中,泛型使用尖括号 < > 来指定。例如,我们可以定义一个列表类型 List<T>,其中的 T 是一种未知的类型。

协变(Covariance)

协变是指类型之间的继承关系可以保持不变,也就是说,如果 AB 的子类型,那么 List<A> 就是 List<B> 的子类型。在 Kotlin 中,我们可以使用 out 关键字来标记类型参数 T,表示 T 是一个协变的类型参数。

我们可以定义一个只读的介于协变类型参数 T 的列表接口,例如:

interface List<out T> {
    operator fun get(index: Int): T
}

通过使用 out 关键字,我们告诉编译器列表 List<T> 是协变的,它可以安全地处理具体类型 T 的子类型。

例如,假设有一个 Person 类和一个 Student 类,Student 继承自 Person。我们可以使用协变的列表接口 List<out T> 作为方法的参数类型,如下所示:

fun printList(list: List<Person>) {
    for (i in 0 until list.size) {
        val person: Person = list[i]
        println(person.name)
    }
}

fun main() {
    val students: List<Student> = listOf(Student("Alice"), Student("Bob"))
    printList(students)
}

由于 List<T> 是协变的,我们可以将 List<Student> 类型的列表传递给接受 List<Person> 类型参数的函数。

逆变(Contravariance)

逆变是指类型之间的继承关系相反,也就是说,如果 AB 的子类型,那么 Consumer<B>Consumer<A> 的子类型。在 Kotlin 中,我们可以使用 in 关键字来标记类型参数 T,表示 T 是一个逆变的类型参数。

我们可以定义一个接受逆变类型参数 T 的消费者接口,例如:

interface Consumer<in T> {
    fun consume(item: T)
}

通过使用 in 关键字,我们告诉编译器消费者接口 Consumer<T> 是逆变的,它可以安全地处理具体类型 T 的父类型。

例如,假设有一个 Person 类和一个 Student 类,Student 继承自 Person。我们可以使用逆变的消费者接口 Consumer<in T> 作为方法的参数类型,如下所示:

fun printNames(consumer: Consumer<Student>) {
    val students: List<Student> = listOf(Student("Alice"), Student("Bob"))
    for (student in students) {
        consumer.consume(student)
    }
}

fun main() {
    val personConsumer: Consumer<Person> = object : Consumer<Person> {
        override fun consume(item: Person) {
            println(item.name)
        }
    }
    printNames(personConsumer)
}

由于 Consumer<T> 是逆变的,我们可以将 Consumer<Person> 类型的消费者传递给接受 Consumer<Student> 类型参数的函数。

总结

协变和逆变是 Kotlin 泛型中的重要概念。通过使用 out 关键字,我们可以标记类型参数为协变,以处理具有继承关系的类型。通过使用 in 关键字,我们可以标记类型参数为逆变,以处理具有相反继承关系的类型。这些特性使得我们可以更加灵活地使用泛型,提高代码的可复用性。

希望本文能够帮助你理解 Kotlin 中的协变与逆变概念,并且在实际开发中应用它们。如有任何疑问或建议,请随时留言。


全部评论: 0

    我有话说: