返回

Kotlin 泛型中的 in 和 out:理解逆变和协变类型

Android

在 Kotlin 中,泛型允许我们创建可用于不同类型数据的代码。这可以提高代码的可重用性并减少重复。

当我们定义泛型时,我们需要使用 in 和 out 来指定泛型类型是逆变的还是协变的。

逆变类型

逆变类型允许我们将子类类型的数据赋值给父类类型的变量。例如,我们可以将一个 List<Dog> 类型的变量赋值给一个 List<Animal> 类型的变量。

val dogs: List<Dog> = listOf(Dog(), Dog())
val animals: List<Animal> = dogs

这是因为 DogAnimal 的子类,因此 List<Dog> 也是 List<Animal> 的子类。

协变类型

协变类型允许我们将父类类型的数据赋值给子类类型的变量。例如,我们可以将一个 Animal 类型的变量赋值给一个 Dog 类型的变量。

val animal: Animal = Dog()
val dog: Dog = animal

这是因为 AnimalDog 的父类,因此 Animal 类型的变量也可以存储 Dog 类型的数据。

in 和 out 的使用

in 关键字用于指定泛型类型是逆变的,out 关键字用于指定泛型类型是协变的。

例如,我们可以这样定义一个逆变的泛型类:

class Invariant<T> {
    fun set(value: T) {
        // ...
    }

    fun get(): T {
        // ...
    }
}

这个类中的 T 类型是逆变的,这意味着我们可以将子类类型的数据赋值给 Invariant 类型的变量。

例如,我们可以这样使用这个类:

val invariant: Invariant<Animal> = Invariant()
invariant.set(Dog())

我们可以将一个 Dog 类型的对象赋值给 invariant 变量,因为 DogAnimal 的子类。

我们也可以这样定义一个协变的泛型类:

class Covariant<out T> {
    fun get(): T {
        // ...
    }
}

这个类中的 T 类型是协变的,这意味着我们可以将父类类型的数据赋值给 Covariant 类型的变量。

例如,我们可以这样使用这个类:

val covariant: Covariant<Dog> = Covariant()
val animal: Animal = covariant.get()

我们可以从 covariant 变量中获取一个 Dog 类型的对象,因为 DogAnimal 的子类。

何时使用 in 和 out

in 和 out 关键字可以帮助我们定义更灵活的泛型代码。

例如,如果我们有一个函数需要接收一个 List<Animal> 类型的参数,我们可以使用一个逆变的泛型类型来定义这个函数,这样我们就可以将 List<Dog> 类型的参数传递给这个函数。

fun printAnimals(animals: List<in Animal>) {
    for (animal in animals) {
        println(animal)
    }
}

这个函数可以使用 List<Dog> 类型的参数来调用:

val dogs: List<Dog> = listOf(Dog(), Dog())
printAnimals(dogs)

如果我们有一个函数需要返回一个 List<Animal> 类型的返回值,我们可以使用一个协变的泛型类型来定义这个函数,这样我们就可以将 List<Dog> 类型的返回值返回给这个函数。

fun getAnimals(): List<out Animal> {
    return listOf(Dog(), Dog())
}

这个函数可以返回 List<Dog> 类型的返回值:

val dogs: List<Dog> = getAnimals()

总结

in 和 out 关键字可以帮助我们定义更灵活的泛型代码。

逆变类型允许我们将子类类型的数据赋值给父类类型的变量,协变类型允许我们将父类类型的数据赋值给子类类型的变量。

我们可以根据需要使用 in 和 out 关键字来定义泛型类型。