一、前言
我们在讲 Java 的《泛型: super,extend,?》一文中,在最后有提到PECS,并有给出一个结论:
那本章还要讲啥呢?
本章讲的是:类型转换(即 赋值)
Kotlin 中泛型引入了 in 和 out:
- 逆变(In)
如果你的类是将泛型对象作为函数的参数,那么可以用 in:
interface Consumer<in T> {
fun consume(item: T)
}
可以称其为 consumer class/interface,因为其主要是消费指定泛型对象。
- 协变(Out)
如果你的类是将泛型作为内部方法的返回,那么可以用 out:
interface Production<out T> {
fun produce(): T
}
可以称其为 production class/interface,因为其主要是产生(produce)指定泛型对象。
- 不变(Invariant)
如果既将泛型作为函数参数,又将泛型作为函数的输出,那就既不用 in 或 out。
interface ProductionConsumer<T> {
fun produce(): T
fun consume(item: T)
}
二、协变(Out)
我们先来举个例子:红苹果! 它既是苹果,又是水果!

代码实现如下:
interface Production<out T> {
fun produce(): T
}
open class Fruit {}
open class Apple: Fruit() {}
class RedApple: Apple() {}
class FruitFactory : Production<Fruit> {
override fun produce(): Fruit {
return Fruit()
}
}
class AppleFactory : Production<Apple> {
override fun produce(): Apple {
return Apple()
}
}
class RedAppleFactory : Production<RedApple> {
override fun produce(): RedApple {
return RedApple()
}
}
// below is OK !
// 子类泛型的对象赋值给使用父类泛型的对象
val factory0 : Production<Fruit> = FruitFactory()
val factory1 : Production<Fruit> = AppleFactory()
val factory2 : Production<Fruit> = RedAppleFactory()
// 父类泛型的对象赋值给使用子类泛型的对象 isn't OK !
val factory00 : Production<RedApple> = FruitFactory() // Error: Type mismatch
val factory01 : Production<RedApple> = AppleFactory() // Error: Type mismatch
val factory02 : Production<RedApple> = RedAppleFactory()
很显然,红苹果工厂属于苹果工厂,也属于水果工厂。
因此,对于 out 泛型,我们能够将使用子类泛型的对象赋值给使用父类泛型的对象。
三、逆变(In)
再根据上面的例子,我们来创建消费者模型:
interface Consumer<in T> {
fun consume(data: T)
}
open class Fruit {}
open class Apple: Fruit() {}
class RedApple: Apple() {}
class People : Consumer<Fruit> {
override fun consume(data: Fruit) {
println("People consume Fruit")
}
}
class Man : Consumer<Apple> {
override fun consume(data: Apple) {
println("People consume Apple")
}
}
class Child : Consumer<RedApple> {
override fun consume(data: RedApple) {
println("People consume RedApple")
}
}
val person0 : Consumer<Apple> = People()
val person1 : Consumer<Apple> = Man()
val person2 : Consumer<Apple> = Child() // Error
很显然,这里的『苹果』消费者只能是『People』和『Man』。
因此,对于 in 泛型,我们能够将使用父类泛型的对象赋值给使用子类泛型的对象。
四、总结
根据以上的内容,我们还可以这样来理解什么时候用 in 和 out:
- 父类泛型对象可以赋值给子类泛型对象,用 in;
- 子类泛型对象可以赋值给父类泛型对象,用 out;

网友评论