當咱們在 Kotlin 中定義泛型時,咱們會發現它須要使用到 in
和 out
兩個關鍵字來定義。從形式上來說,這是一種定義「逆變」和「協變」的方式。ide
那啥叫逆變?啥叫協變?能夠參考下維基百科的定義:協變與逆變函數
out(協變)spa
若是泛型類只將泛型類型做爲函數的返回(輸出),那麼使用 out:code
interface Production<out T> {
fun produce(): T
}
複製代碼
能夠稱之爲生產類/接口,由於它主要是用來生產(produce)指定的泛型對象。所以,咱們能夠簡單地這樣記憶:對象
produce = output = out接口
in(逆變)ip
若是泛型類只將泛型類型做爲函數的入參(輸入),那麼使用 in:get
interface Consumer<in T> {
fun consume(item: T)
}
複製代碼
能夠稱之爲消費者類/接口,由於它主要是用來消費(consume)指定的泛型對象。所以咱們能夠簡單地這樣記憶:input
consume = input = instring
invariant(不變)
若是泛型類既將泛型類型做爲函數參數,又將泛型類型做爲函數的輸出,那麼既不用 out 也不用 in:
interface ProductionConsumer<T> {
fun produce(): T
fun consume(item: T)
}
複製代碼
舉個例子,咱們定義下漢堡類對象,它是一種快餐,也是一種食物。
open class Food
open class FastFood : Food()
class Burger : FastFood()
複製代碼
根據上面定義的生產(Production)接口,咱們能夠進一步擴展它們來生產食物、快餐和漢堡:
class FoodStore : Production<Food> {
override fun produce(): Food {
println("Produce food")
return Food()
}
}
class FastFoodStore : Production<FastFood> {
override fun produce(): FastFood {
println("Produce fast food")
return FastFood()
}
}
class InOutBurger : Production<Burger> {
override fun produce(): Burger {
println("Produce burger")
return Burger()
}
}
複製代碼
如今,咱們能夠這樣賦值:
val production1 : Production<Food> = FoodStore()
val production2 : Production<Food> = FastFoodStore()
val production3 : Production<Food> = InOutBurger()
複製代碼
顯然,漢堡商店屬於快餐商店,也屬於食物商店。
所以,對於 out 類型,咱們可以將使用子類泛型的對象賦值給使用父類泛型的對象。
若是咱們修改以下,那麼就會出錯了,由於食物或快餐商店是能夠生產漢堡,但不必定僅僅生產漢堡:
val production1 : Production<Burger> = FoodStore() // Error
val production2 : Production<Burger> = FastFoodStore() // Error
val production3 : Production<Burger> = InOutBurger()
複製代碼
根據上面定義的消費(Consumer)接口,咱們能夠進一步擴展它們來消費食物、快餐和漢堡:
class Everybody : Consumer<Food> {
override fun consume(item: Food) {
println("Eat food")
}
}
class ModernPeople : Consumer<FastFood> {
override fun consume(item: FastFood) {
println("Eat fast food")
}
}
class American : Consumer<Burger> {
override fun consume(item: Burger) {
println("Eat burger")
}
}
複製代碼
咱們能夠將人類、現代人、美國人指定爲漢堡消費者,因此能夠這樣賦值:
val consumer1 : Consumer<Burger> = Everybody()
val consumer2 : Consumer<Burger> = ModernPeople()
val consumer3 : Consumer<Burger> = American()
複製代碼
不難理解,漢堡的消費者能夠是美國人,也能夠是現代人,更能夠是人類。
所以,對於 in 泛型,咱們可以將使用父類泛型的對象賦值給使用子類泛型的對象。
反之,若是咱們修改以下,就會出現錯誤,由於漢堡的消費者不單單是美國人或現代人。
val consumer1 : Consumer<Food> = Everybody()
val consumer2 : Consumer<Food> = ModernPeople() // Error
val consumer3 : Consumer<Food> = American() // Error
複製代碼
參考資料: