# 協(xié)變和逆變的概念及示例
## 協(xié)變(Covariance)
協(xié)變允許使用子類型的實(shí)例代替泛型類型的實(shí)例个榕。例如,如果 `Dog` 是 `Animal` 的子類,那么 `List<Dog>` 可以被視為 `List<Animal>` 的一個特化版本封恰。
### 示例
假設(shè)我們有一個函數(shù),它接受一個 `List<Animal>` 并打印出每個動物的名字:
```kotlin
fun printAnimalNames(animals: List<Animal>) {
animals.forEach { println(it.name) }
}
如果某人有一個 List<Dog>
并想使用這個函數(shù)褐啡,協(xié)變使得這成為可能:
val dogs: List<Dog> = listOf(Dog("Buddy"), Dog("Max"))
printAnimalNames(dogs) // 正確使用诺舔,因?yàn)?List<Dog> 是 List<Animal> 的協(xié)變
圖解
List<Animal>
^
|
List<Dog> <-- 協(xié)變關(guān)系
逆變(Contravariance)
逆變允許使用超類型的實(shí)例代替泛型類型的實(shí)例。例如备畦,如果 Dog
是 Animal
的子類低飒,那么 List<Animal>
可以被視為 List<Dog>
的一個泛化版本。
示例
假設(shè)我們有一個函數(shù)懂盐,它接受一個 List<Dog>
并為每只狗洗澡:
fun batheDogs(dogs: List<Dog>) {
dogs.forEach { it.bathe() }
}
如果某人有一個 List<Animal>
并想使用這個函數(shù)褥赊,逆變在這里是不適用的:
val animals: List<Animal> = listOf(Dog(), Cat())
batheDogs(animals as List<Dog>) // 錯誤:不能直接轉(zhuǎn)換,因?yàn)椴粷M足逆變
圖解
List<Dog>
^
|
List<Animal> <-- 逆變關(guān)系(不直接支持)
星號投影(Star Projection)
星號投影允許我們忽略泛型的具體類型莉恼,它是一種特殊的協(xié)變形式拌喉。這在你需要操作泛型集合速那,但不需要關(guān)心其具體類型時非常有用。
示例
假設(shè)我們有一個函數(shù)尿背,它接受任何類型的 List<*>
并打印出元素的數(shù)量:
fun printListSize(list: List<*>) {
println("The list has ${list.size} elements")
}
無論是 List<Dog>
端仰、List<Animal>
還是任何其他類型的列表,都可以傳遞給這個函數(shù):
val dogs: List<Dog> = listOf(Dog(), Dog())
val animals: List<Animal> = listOf(Dog(), Cat())
printListSize(dogs) // 正確使用
printListSize(animals) // 也正確使用
圖解
List<*>
星號投影允許我們以一種類型安全的方式處理未知類型的集合田藐。
結(jié)論
協(xié)變和逆變提供了一種機(jī)制榆俺,使得泛型類型可以更加靈活地使用。協(xié)變允許我們使用更具體的類型作為參數(shù)坞淮,而逆變允許我們使用更一般的類型作為參數(shù)茴晋。然而,逆變需要謹(jǐn)慎使用回窘,以避免違反類型安全诺擅。星號投影提供了一種折衷方案,允許我們在不需要具體類型信息時操作集合啡直。