如何让Kotlin允许返回泛型为预期返回值子类型的对象?
问题分析与解决方案
首先,你的代码编译失败的核心原因是Kotlin泛型默认是不变的:即使B继承自A,Baz<B>和Baz<A>之间也不存在任何继承或兼容关系,所以无法直接将Baz<B>作为Baz<A>返回。
为什么编译器不允许自动推断?
假设允许这种隐式转换,考虑如果Baz包含可写的属性(比如var value: T),那么后续可能出现这样的操作:
val bazA: Baz<A> = doBar() // 假设允许转换 bazA.value = C() // C是A的另一个子类
此时bazA实际指向的是Baz<B>实例,却被赋值了C类型的对象,这会导致类型安全问题。为了避免这种情况,Kotlin默认禁止泛型类型的协变转换。
无需手动转换的解决方案
因为你的Baz类中value是只读的val,没有修改T类型的操作,适合将Baz定义为协变泛型:在泛型参数前添加out关键字,声明T是一个协变类型参数。
修改后的代码如下:
open class A class B: A() // 添加out关键字使Baz协变 class Baz<out T>(val value: T) fun doFoo(): Baz<A> { return doBar() // 现在可以正常编译 } fun doBar(): Baz<B> { return Baz(B()) }
这样修改后,Baz<B>会被视为Baz<A>的子类型,编译器允许隐式转换,同时完全保留类型安全性——因为协变泛型只允许对外暴露T类型的输出(比如只读属性、返回T的方法),不允许接收T类型的输入(比如var属性、参数为T的方法),从根本上避免了上述的类型安全问题。
你之前尝试将A改为接口无效,是因为问题的根源不在A的类型(类或接口),而在于Baz的泛型不变性,修改泛型的协变属性就能解决问题。
内容的提问来源于stack exchange,提问作者user2084865
相关产品推荐
相关产品推荐

