Kotlin上下文敏感泛型:out修饰符下的编译行为文档咨询
Kotlin泛型协变变量分支内可调用消费方法的官方说明
首先贴出你提供的简化代码:
open class Fruit class Apple : Fruit() class Orange : Fruit() abstract class Helper<T : Fruit> { open fun save(something: T) {} } class AppleHelper : Helper<Apple>() { override fun save(something: Apple) {} } class OrangeHelper : Helper<Orange>() { override fun save(something: Orange) {} } fun process(theFruit: Fruit) { val myHelper: Helper<out Fruit> when (theFruit) { is Apple -> { myHelper = AppleHelper() myHelper.save(theFruit) // this does compile (NOT expected) } is Orange -> { myHelper = OrangeHelper() myHelper.save(theFruit) // this does compile (NOT expected) } else -> throw RuntimeException() } myHelper.save(theFruit) // this doesn't compile (as expected) }
现象解析
你遇到的情况是Kotlin编译器的智能类型细化与泛型上下文类型推断共同作用的结果,核心逻辑如下:
- 在
when的is Apple/is Orange分支中,编译器会将theFruit的类型细化为对应子类,同时,由于你将具体的AppleHelper/OrangeHelper赋值给myHelper,编译器在当前分支代码块内会把myHelper的实际类型推断为Helper<Apple>/Helper<Orange>——尽管它的声明类型是Helper<out Fruit>,但局部上下文里能100%确定具体类型。 - 协变(
out修饰)的限制是“不能随意用泛型参数作为方法入参”,但这里编译器明确知道myHelper的泛型参数与传入的theFruit类型完全匹配,因此允许调用save方法。 - 离开分支后,
myHelper的类型回到声明的Helper<out Fruit>,编译器无法确定它的具体泛型参数,theFruit也回到Fruit类型,此时调用save会违反协变限制,因此无法编译,符合预期。
官方文档对应说明(中文翻译)
Kotlin官方文档的智能转换与泛型协变章节中明确了这个行为:
编译器能够在局部代码块内,根据类型检查、赋值操作等语句,细化变量的实际类型。对于泛型变量,当上下文能完全确定其具体类型参数时,编译器会放宽协变/逆变的限制,允许在类型安全的前提下调用原本被限制的方法。这种细化仅在当前代码块内有效,离开代码块后,变量恢复到声明的泛型类型。
关于协变的补充说明:
使用
out修饰的泛型类型参数,通常仅允许作为返回值(生产者)使用,但如果编译器在局部上下文中能绝对确定泛型参数的具体类型,且传入的方法参数与该具体类型完全匹配,那么允许调用消费型方法,这是编译器的类型安全优化机制。
内容的提问来源于stack exchange,提问作者Forge_7
相关产品推荐
相关产品推荐

