
簡評:在 Kotlin 中使用泛型你會注意到其中引入了 in 和 out,對于不熟悉的開發(fā)者來說可能有點難以理解。從形式上講,這是一種定義逆變和協(xié)變的方式,這篇文章就來講講怎么來理解和記住它們。
in & out 怎么記?
Out (協(xié)變)
如果你的類是將泛型作為內(nèi)部方法的返回,那么可以用 out:
interface Production<out T> {
fun produce(): T
}
可以稱其為 production class/interface,因為其主要是產(chǎn)生(produce)指定泛型對象。因此,可以這樣來記:produce = output = out。
In(逆變)
如果你的類是將泛型對象作為函數(shù)的參數(shù),那么可以用 in:
interface Consumer<in T> {
fun consume(item: T)
}
可以稱其為 consumer class/interface,因為其主要是消費指定泛型對象。因此,可以這樣來記:consume = input = in。
Invariant(不變)
如果既將泛型作為函數(shù)參數(shù),又將泛型作為函數(shù)的輸出,那就既不用 in 或 out。
interface ProductionConsumer<T> {
fun produce(): T
fun consume(item: T)
}
舉個例子
假設(shè)我們有一個漢堡(burger)對象,它是一種快餐,當然更是一種食物。

open class Food
open class FastFood : Food()
class Burger : FastFood()
1. 漢堡提供者
根據(jù)上面定義的類和接口來設(shè)計提供 food, fastfood 和 burger 的類:
class FoodStore : Production<Food> {
override fun produce(): Food {
println("Produce food")
return Food()
}
}
class FastFoodStore : Production<FastFood> {
override fun produce(): FastFood {
println("Produce food")
return FastFood()
}
}
class InOutBurger : Production<Burger> {
override fun produce(): Burger {
println("Produce burger")
return Burger()
}
}
現(xiàn)在,我們可以這樣賦值:
val production1 : Production<Food> = FoodStore()
val production2 : Production<Food> = FastFoodStore()
val production3 : Production<Food> = InOutBurger()
很顯然,漢堡商店屬于是快餐商店,當然也屬于食品商店。
因此,對于 out 泛型,我們能夠?qū)⑹褂米宇惙盒偷膶ο筚x值給使用父類泛型的對象。
而如果像下面這樣反過來使用子類 - Burger 泛型,就會出現(xiàn)錯誤,因為快餐(fastfood)和食品(food)商店不僅僅提供漢堡(burger)。
val production1 : Production<Burger> = FoodStore() // Error
val production2 : Production<Burger> = FastFoodStore() // Error
val production3 : Production<Burger> = InOutBurger()
2. 漢堡消費者
再讓我們根據(jù)上面的類和接口來定義漢堡消費者類:
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")
}
}
現(xiàn)在,我們能夠?qū)?Everybody, ModernPeople 和 American 都指定給漢堡消費者(Consumer<Burger>):
val consumer1 : Consumer<Burger> = Everybody()
val consumer2 : Consumer<Burger> = ModernPeople()
val consumer3 : Consumer<Burger> = American()
很顯然這里美國的漢堡的消費者既是現(xiàn)代人,更是人類。
因此,對于 in 泛型,我們可以將使用父類泛型的對象賦值給使用子類泛型的對象。
同樣,如果這里反過來使用父類 - Food 泛型,就會報錯:
val consumer1 : Consumer<Food> = Everybody()
val consumer2 : Consumer<Food> = ModernPeople() // Error
val consumer3 : Consumer<Food> = American() // Error
根據(jù)以上的內(nèi)容,我們還可以這樣來理解什么時候用 in 和 out:
- 父類泛型對象可以賦值給子類泛型對象,用 in;
- 子類泛型對象可以賦值給父類泛型對象,用 out。
