返回

深入解析Kotlin泛型型变:协变、逆变与不变

Android

协变

协变 是指子类型可以替换其父类型的位置,而不会破坏类型安全性。在 Kotlin 中,协变类型用 "out" 声明。

示例:

考虑一个 List 类,它存储特定类型的元素。如果我们有一个 List<Parent> 和一个 List<Child>,其中 ChildParent 的子类,那么 List<Child> 可以安全地替换 List<Parent>,因为 Child 可以存储任何 Parent 可以存储的内容。因此,List<Child>List<Parent> 的协变类型。

逆变

逆变 是指父类型可以替换其子类型的位置,而不会破坏类型安全性。在 Kotlin 中,逆变类型用 "in" 关键字声明。

示例:

考虑一个 Comparator 类,它将两个元素进行比较。如果我们有一个 Comparator<Child> 和一个 Comparator<Parent>,那么 Comparator<Child> 可以安全地替换 Comparator<Parent>,因为 Comparator<Child> 可以比较任何 Parent 可以比较的元素。因此,Comparator<Child>Comparator<Parent> 的逆变类型。

不变

不变 是指子类型不能替换其父类型,或者父类型不能替换其子类型。在 Kotlin 中,不变类型没有显式声明。

示例:

考虑一个 Set 类,它存储唯一元素。如果我们有一个 Set<Parent> 和一个 Set<Child>,那么 Set<Child> 不能替换 Set<Parent>,因为 Set<Child> 中的元素可能重复出现,而 Set<Parent> 中的元素不能重复出现。因此,Set<Child>Set<Parent> 都是不变类型。

协变和逆变的应用

协变和逆变在 Kotlin 中有广泛的应用:

  • 协变类型: 用于表示可以安全地存储子类型对象的集合或容器类型。例如, List<Parent> 可以安全地存储 Child 对象。
  • 逆变类型: 用于表示可以安全地比较或处理子类型对象的函数或比较器类型。例如, Comparator<Child> 可以安全地比较 Parent 对象。

协变、逆变和不变的限制

虽然协变和逆变可以提高代码的灵活性,但也存在一些限制:

  • 协变: 协变类型不能用于修改底层数据结构。例如,虽然 List<Child>List<Parent> 的协变类型,但我们不能将 Child 对象添加到 List<Parent> 中,因为这会破坏类型安全性。
  • 逆变: 逆变类型不能用于生产类型。例如,虽然 Comparator<Child>Comparator<Parent> 的逆变类型,但我们不能使用 Comparator<Child> 创建新的 Child 对象,因为这会破坏类型安全性。
  • 不变: 不变类型在子类型化中没有灵活性。例如,Set<Child>Set<Parent> 是不变类型,因此它们不能相互替换,即使 ChildParent 的子类。

结论

协变、逆变和不变是 Kotlin 泛型型变中的关键概念。通过理解这些概念,我们可以编写出更安全、更健壮的代码。然而,了解它们的限制也同样重要,以避免潜在的类型安全问题。