首页
学习
活动
专区
工具
TVP
发布

Kotlin 泛型中的 in和out

价值 思考 共鸣

简评:在 Kotlin 中使用泛型你会注意到其中引入了 in 和 out,对于不熟悉的开发者来说可能有点难以理解。从形式上讲,这是一种定义逆变和协变的方式,这篇文章就来讲讲怎么来理解和记住它们。

in & out 怎么记?

Out (协变)

如果你的类是将泛型作为内部方法的返回,那么可以用 out:

interface Production {

fun produce(): T

}

可以称其为 production class/interface,因为其主要是产生(produce)指定泛型对象。因此,可以这样来记:produce = output = out。

In(逆变)

如果你的类是将泛型对象作为函数的参数,那么可以用 in:

interface Consumer {

fun consume(item: T)

}

可以称其为 consumer class/interface,因为其主要是消费指定泛型对象。因此,可以这样来记:consume = input = in。

Invariant(不变)

如果既将泛型作为函数参数,又将泛型作为函数的输出,那就既不用 in 或 out。

interface ProductionConsumer {

fun produce(): T

fun consume(item: T)

}

举个例子

假设我们有一个汉堡(burger)对象,它是一种快餐,当然更是一种食物。

open class Foodopen class FastFood : Food() class Burger : FastFood()

1. 汉堡提供者

根据上面定义的类和接口来设计提供food, fastfood和burger的类:

class FoodStore : Production {

override fun produce(): Food {

println("Produce food")

return Food()

}

}class FastFoodStore : Production {

override fun produce(): FastFood {

println("Produce food")

return FastFood()

}

}class InOutBurger : Production {

override fun produce(): Burger {

println("Produce burger")

return Burger()

}

}

现在,我们可以这样赋值:

val production1 : Production = FoodStore()

val production2 : Production = FastFoodStore()

val production3 : Production = InOutBurger()

很显然,汉堡商店属于是快餐商店,当然也属于食品商店。

因此,对于 out 泛型,我们能够将使用子类泛型的对象赋值给使用父类泛型的对象。

而如果像下面这样反过来使用子类 - Burger 泛型,就会出现错误,因为快餐(fastfood)和食品(food)商店不仅仅提供汉堡(burger)。

val production1 : Production = FoodStore() // Error

val production2 : Production = FastFoodStore() // Error

val production3 : Production = InOutBurger()

2. 汉堡消费者

再让我们根据上面的类和接口来定义汉堡消费者类:

class Everybody : Consumer {

override fun consume(item: Food) {

println("Eat food")

}

}class ModernPeople : Consumer {

override fun consume(item: FastFood) {

println("Eat fast food")

}

}class American : Consumer {

override fun consume(item: Burger) {

println("Eat burger")

}

}

现在,我们能够将Everybody, ModernPeople和 American 都指定给汉堡消费者(Consumer):

val consumer1 : Consumer = Everybody()

val consumer2 : Consumer = ModernPeople()

val consumer3 : Consumer = American()

很显然这里美国的汉堡的消费者既是现代人,更是人类。

因此,对于 in 泛型,我们可以将使用父类泛型的对象赋值给使用子类泛型的对象。

同样,如果这里反过来使用父类 - Food 泛型,就会报错:

val consumer1 : Consumer = Everybody()

val consumer2 : Consumer = ModernPeople() // Error

val consumer3 : Consumer = American() // Error

根据以上的内容,我们还可以这样来理解什么时候用 in 和 out:

父类泛型对象可以赋值给子类泛型对象,用 in;

子类泛型对象可以赋值给父类泛型对象,用 out。

英文原文:In and out type variant of Kotlin

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180108G0NP9N00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券