为何Kotlin中out T泛型限制会禁止返回T的函数参数?
问题解释与示例
核心原因
Kotlin 中out T的协变规则是:泛型参数T只能出现在输出位置(类的成员返回值、只读属性类型),绝对不能出现在输入位置(函数参数的类型定义中)。
你觉得() -> T里的T是返回值属于out位置,但忽略了一个关键:() -> T是bar函数的入参类型,而非Foo类对外输出的类型。对于Foo<out T>的协变语义来说,只要外部能向Foo的成员函数传递包含T的参数,就会破坏协变的类型安全承诺。
更本质的逻辑是:() -> T本身是协变类型(因为T是返回值),但当它作为函数参数时,对于Foo的泛型T来说,相当于把T放在了逆变位置——允许这样的写法,会让协变的类型安全边界被打破。
破坏方差语义的示例
我们用类继承关系来模拟这个场景:
open class Animal class Cat : Animal() class Dog : Animal() // 假设编译器允许这段代码通过(实际会报错) class Foo<out T> { fun bar(f: () -> T): T { return f() } } fun main() { // 协变规则允许 Foo<Cat> 赋值给 Foo<Animal> val fooAnimal: Foo<Animal> = Foo<Cat>() // 给 bar 传入一个返回 Dog 的函数(Dog 是 Animal 的子类,符合 () -> Animal 的类型要求) val result: Animal = fooAnimal.bar { Dog() } // 尝试将结果强转为 Cat,运行时直接抛出 ClassCastException val cat: Cat = result as Cat }
这段代码的问题在于:
- 协变规则保证
Foo<Cat>可以安全当作Foo<Animal>使用 - 如果允许
bar接收() -> T作为参数,我们就能向fooAnimal传入返回Dog的函数 - 但原
Foo<Cat>的bar函数本该返回Cat类型,实际却返回了Dog,最终导致类型转换崩溃——完全违背了协变的类型安全承诺。
内容的提问来源于stack exchange,提问作者user3612643
相关产品推荐
相关产品推荐

