Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
  • 协变与逆变(out 和 in)

协变与逆变(out 和 in)

1. 泛型的不变性问题

在默认情况下,Kotlin 的泛型是不变的(invariant)。这意味着即使类型 B 是类型 A 的子类型,Generic<B> 也不是 Generic<A> 的子类型。例如:

class Box<T>(var item: T)

val stringBox: Box<String> = Box("Hello")
// 编译错误:类型不匹配
val anyBox: Box<Any> = stringBox 

这种设计是为了保证类型安全,但限制了泛型的灵活性。

2. 协变(out)

协变(covariance)允许泛型类型参数在子类型关系中保持相同的方向。使用 out 关键字标记类型参数:

class Producer<out T>(private val item: T) {
    fun produce(): T = item
}

val stringProducer: Producer<String> = Producer("Kotlin")
// 合法:Producer<String> 是 Producer<Any> 的子类型
val anyProducer: Producer<Any> = stringProducer

关键规则:

  • out 表示类型参数只能作为输出(返回值)
  • 禁止作为函数参数(除了构造函数)

3. 逆变(in)

逆变(contravariance)允许泛型类型参数在子类型关系中反转方向。使用 in 关键字标记类型参数:

class Consumer<in T> {
    fun consume(item: T) {
        println("Consuming $item")
    }
}

val anyConsumer: Consumer<Any> = Consumer()
// 合法:Consumer<Any> 是 Consumer<String> 的子类型
val stringConsumer: Consumer<String> = anyConsumer

关键规则:

  • in 表示类型参数只能作为输入(函数参数)
  • 禁止作为返回值类型

4. 使用场景对比

特性关键字类型关系典型应用场景
协变outProducer<Cat> → Producer<Animal>生产者类(如集合读取)
逆变inComparator<Animal> → Comparator<Cat>消费者类(如比较器)

5. 类型投影(Use-site Variance)

当无法修改类声明时,可以在使用处指定型变:

fun copy(from: Array<out Any>, to: Array<Any>) { /*...*/ }

val ints: Array<Int> = arrayOf(1, 2, 3)
val any = Array<Any>(3) { "" }
copy(ints, any) // 允许协变

6. 星投影(Star Projection)

当类型参数未知时使用 *:

fun printItems(list: List<*>) {
    list.forEach { println(it) }
}

等价于 List<out Any?>,表示不确定具体类型但安全读取。

7. 实际应用示例

// 协变示例:Kotlin 的 List 接口
public interface List<out E> : Collection<E> {
    // 只有返回 E 的方法
}

// 逆变示例:Comparable 接口
public interface Comparable<in T> {
    operator fun compareTo(other: T): Int
}

最佳实践:优先考虑声明处型变(class Box<out T>),只有在无法修改类声明时才使用使用处型变(Box<out T>)。

Last Updated:: 5/21/25, 7:58 PM