kotlin的in和out关键字本质对应java的? super t和? extends t:in用于消费者场景(只输入,逆变),out用于生产者场景(只输出,协变),均遵循pecs原则——producer extends、consumer super,确保类型安全。

Kotlin 的 in 和 out 关键字,本质上就是 Java 泛型中 ? super T 和 ? extends T 的直接映射,只是语法更简洁、语义更明确。
in ↔ ? super T(逆变,消费者场景)
当泛型类型只用于“输入”或“消费”数据(比如往里塞东西、作为参数传入),就用 in —— 它对应 Java 的 ? super T。
- Java 写法:
List super String>表示可以存 String 及其所有父类(如Object、CharSequence)的实例 - Kotlin 写法:
fun consume(item: Consumer<in string>)</in>,意味着这个Consumer能接受String,也能接受更宽泛的类型(比如Any) - 核心逻辑:能处理父类型的消费者,自然也能处理子类型(向上兼容)
out ↔ ? extends T(协变,生产者场景)
当泛型类型只用于“输出”或“提供”数据(比如从容器里取值、作为返回值),就用 out —— 它对应 Java 的 ? extends T。
- Java 写法:
List extends Number>表示里面元素是 Number 或其任意子类(如Integer、Double),但不能往里 add(除了null) - Kotlin 写法:
fun produce(): Producer<out number></out>,表示它产出的是Number或其子类,调用方可以安全地当作Number使用 - 核心逻辑:子类型的实例可以安全赋值给父类型引用(向下兼容读取)
为什么这样设计?PECS 原则直击本质
无论 Java 还是 Kotlin,底层逻辑都统一在一条经验法则上:
-
Producer Extends → out / ? extends:你往外“产”数据,类型要放宽(允许子类),所以用
out或? extends -
Consumer Super → in / ? super:你往里“收”数据,类型要收窄(允许父类接收),所以用
in或? super
这不是语法巧合,而是类型系统对“读写角色”的强制约束 —— 防止把 Integer 放进声明为 List<string></string> 的容器,也防止从 List<number></number> 里取出却当成 String 用。
实际用法对比示例
假设有个接口 Comparator<t></t>,它只消费(接收两个 T 比较),不产出 T:
- Java:
Comparator super Person>→ 可以传入Comparator<object></object>或Comparator<person></person>,但不能传Comparator<student></student>(太窄) - Kotlin:
Comparator<in person></in>→ 同样,Comparator<any></any>可赋值给它,因为Any能比较Person
再比如 ReadOnlyList<t></t> 只提供 get(),不提供 add():
- Java:
ReadOnlyList extends Animal>→ 可以持有Dog、Cat等列表 - Kotlin:
ReadOnlyList<out animal></out>→ReadOnlyList<dog></dog>可安全转为ReadOnlyList<animal></animal>
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











