Kotlin 泛型中的 in 和 out:理解逆变和协变类型
2023-12-27 00:40:14
在 Kotlin 中,泛型允许我们创建可用于不同类型数据的代码。这可以提高代码的可重用性并减少重复。
当我们定义泛型时,我们需要使用 in 和 out 来指定泛型类型是逆变的还是协变的。
逆变类型
逆变类型允许我们将子类类型的数据赋值给父类类型的变量。例如,我们可以将一个 List<Dog>
类型的变量赋值给一个 List<Animal>
类型的变量。
val dogs: List<Dog> = listOf(Dog(), Dog())
val animals: List<Animal> = dogs
这是因为 Dog
是 Animal
的子类,因此 List<Dog>
也是 List<Animal>
的子类。
协变类型
协变类型允许我们将父类类型的数据赋值给子类类型的变量。例如,我们可以将一个 Animal
类型的变量赋值给一个 Dog
类型的变量。
val animal: Animal = Dog()
val dog: Dog = animal
这是因为 Animal
是 Dog
的父类,因此 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
变量,因为 Dog
是 Animal
的子类。
我们也可以这样定义一个协变的泛型类:
class Covariant<out T> {
fun get(): T {
// ...
}
}
这个类中的 T
类型是协变的,这意味着我们可以将父类类型的数据赋值给 Covariant
类型的变量。
例如,我们可以这样使用这个类:
val covariant: Covariant<Dog> = Covariant()
val animal: Animal = covariant.get()
我们可以从 covariant
变量中获取一个 Dog
类型的对象,因为 Dog
是 Animal
的子类。
何时使用 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 关键字来定义泛型类型。