Kotlin 泛型中的 in 和 out

原创 2018年04月16日 10:15:34

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

in & out 怎么记?

Out (协变)

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

interface Production<out T> {

    fun produce(): T
}

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

In(逆变)

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

interface Consumer<in T> {

    fun consume(item: T)

}

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

Invariant(不变)

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

interface ProductionConsumer<T> {

    fun produce(): T
   fun consume(item: T)

}

举个例子

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

0?wx_fmt=jpeg

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

1. 汉堡提供者

根据上面定义的类和接口来设计提供 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()
   }
}

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

val production1 : Production<Food> = FoodStore()
val production2 : Production<Food> = FastFoodStore()
val production3 : Production<Food> = InOutBurger()

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

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

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

val production1 : Production<Burger> = FoodStore()  // Error
val production2 : Production<Burger> = FastFoodStore()  // Error
val production3 : Production<Burger> = InOutBurger()

2. 汉堡消费者

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

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")
   }
}

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

val consumer1 : Consumer<Burger> = Everybody()
val consumer2 : Consumer<Burger> = ModernPeople()
val consumer3 : Consumer<Burger> = American()

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

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

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

val consumer1 : Consumer<Food> = Everybody()
val consumer2 : Consumer<Food> = ModernPeople()  // Error
val consumer3 : Consumer<Food> = American()  // Error

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

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

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

0?wx_fmt=jpeg

英文原文In and out type variant of Kotlin

Kotlin语法(十二)-泛型(Generics)

参考原文:http://kotlinlang.org/docs/reference/generics.html      泛型类          跟Java一样,Kotlin也支持泛型类: ...
  • tangxl2008008
  • tangxl2008008
  • 2016-09-03 10:11:48
  • 6404

Step into Kotlin - 17 - 泛型

定义泛型,使用泛型,in 与 out,逆变与协变,集合的边界
  • mrseasons
  • mrseasons
  • 2015-07-21 09:45:57
  • 1285

4.1 Java语言和Kotlin语言对比(2) 泛型和数组

4.1 Java语言和Kotlin语言对比(2) 注意Java中基础类型的装箱类型当用作类型参数时被映射成了平台类型。 例如,List 在Kotlin中被映射成List 集合类型在Kotl...
  • farmer_cc
  • farmer_cc
  • 2017-05-31 10:28:38
  • 614

kotlin 泛型学习笔记

kotlin中泛型学习笔记 翻译自《Programing Kotlin》第八章,并进行了一些修改
  • u012227177
  • u012227177
  • 2017-06-09 09:38:39
  • 1251

Kotlin 泛型的协变和逆变

Kotlin 泛型Kotlin 泛型的基本语法类似于 Java ,不过出于型变安全,不支持 Java 中的&amp;lt;? extends T&amp;gt;,&amp;lt;?super T&am...
  • Al_assad
  • Al_assad
  • 2017-09-20 00:43:54
  • 1050

Kotlin 中的泛型

点此进入:从零快速构建APP系列目录导图点此进入:UI编程系列目录导图点此进入:四大组件系列目录导图点此进入:数据网络和线程系列目录导图一、泛型基础泛型编程包括,在不指定代码中使用到的确切类型的情况下...
  • ComWill
  • ComWill
  • 2017-09-22 14:29:13
  • 353

Kotlin-泛型

概述一般类和函数,只能使用具体的类型:要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的约束对代码的限制很大。而OOP的多态采用了一种泛化的机制,在SE 5种,Java引...
  • IO_Field
  • IO_Field
  • 2016-12-11 17:38:59
  • 6973

Kotlin 官方学习教程之密封类与泛型

Kotlin 官方学习教程之密封类与泛型
  • jim__charles
  • jim__charles
  • 2017-06-18 19:02:34
  • 402

Kotlin Reference (十一) 泛型、数组型变、泛型型变、泛型约束

型变特性,分变和不变;变又分协变与逆变 简单的描述 协变与逆变,它们有一个共同的前提,即出现在"有继承或实现"关系的一组类型中。 协变:父类出现的地方,可以用子类代替(符合面向对象的基本原则,里氏...
  • jjwwmlp456
  • jjwwmlp456
  • 2017-07-09 04:12:59
  • 2075

C#深入学习:泛型修饰符in,out、逆变委托类型和协变委托类型

在C#中,存在两个泛型修饰符:in和out,他们分别对应逆变委托和协变委托。 我们知道,在C#中要想将一个泛型对象转换为另一个泛型对象时,必须要将一个泛型对象拆箱,对元素进行显式或隐式转换后重新装箱...
  • mykge
  • mykge
  • 2015-01-20 16:29:03
  • 1330
收藏助手
不良信息举报
您举报文章:Kotlin 泛型中的 in 和 out
举报原因:
原因补充:

(最多只允许输入30个字)