Kotlin 泛型中的 in 和 out

當咱們在 Kotlin 中定義泛型時,咱們會發現它須要使用到 inout 兩個關鍵字來定義。從形式上來說,這是一種定義「逆變」和「協變」的方式。ide

那啥叫逆變?啥叫協變?能夠參考下維基百科的定義:協變與逆變函數

in & out 怎麼記?

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)
}
複製代碼

爲啥要使用 in & out ?

舉個例子,咱們定義下漢堡類對象,它是一種快餐,也是一種食物。

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
複製代碼

記住 in & out 的另外一種方式

  • 父類泛型對象能夠賦值給子類泛型對象,用 in;
  • 子類泛型對象能夠賦值給父類泛型對象,用 out。

參考資料:

In and out type variant of Kotlin

Kotlin 泛型中的 in 和 out

相關文章
相關標籤/搜索