Kotlin泛型问题:BaseResp<BaseCode.Data>与BaseCode的类型差异及优化
问题分析与优化方案
编译错误根源
BaseCode继承自BaseResp<BaseCode.Data>,但BaseResp是不变泛型,因此BaseResp<BaseCode.Data>并非BaseResp<Any>的子类,导致BaseCode无法满足send函数中T:BaseResp<Any>的泛型约束,最终编译失败。
以下是几种无需冗余调用的优化方案:
方案1:让BaseResp支持协变(推荐,业务允许时优先选择)
将BaseResp的泛型标记为协变(out T),这样BaseResp<SubType>可以安全向上转型为BaseResp<SuperType>,同时调整send的泛型约束,适配任意BaseResp子类:
open class BaseResp<out T> { // 协变泛型下,T只能作为返回值类型,因此var需改为val(若必须用var,需确保setter不对外暴露) val data: T? = null } interface IMessageCallback<T> { fun onMessage(data: T) } // 调整泛型约束为T继承自BaseResp<*>,而非BaseResp<Any> inline fun <reified T : BaseResp<*>> send(cb: IMessageCallback<T>?) {} fun test(cb: IMessageCallback<BaseCode>?) { send(cb) // 编译通过,调用简洁无冗余 } class BaseCode : BaseResp<BaseCode.Data>() { inner class Data { var code = 0 } }
方案2:通过双重泛型推导简化调用
如果无法修改BaseResp的协变性,可给send函数增加泛型参数,让编译器自动推导BaseResp的内部泛型类型,避免调用者手动指定:
open class BaseResp<T> { var data: T? = null } interface IMessageCallback<T> { fun onMessage(data: T) } // 新增泛型D,约束T必须是BaseResp<D>的子类 inline fun <reified T, reified D> send(cb: IMessageCallback<T>?) where T : BaseResp<D> {} fun test(cb: IMessageCallback<BaseCode>?) { send(cb) // 编译器自动推导T=BaseCode,D=BaseCode.Data,无需冗余代码 } class BaseCode : BaseResp<BaseCode.Data>() { inner class Data { var code = 0 } }
方案3:调整IMessageCallback的泛型逆变(按需选择)
如果send函数仅需消费T类型数据(即调用onMessage),可让IMessageCallback支持逆变(in T),实现子类回调向父类约束的兼容:
open class BaseResp<T> { var data: T? = null } // 逆变泛型in T,表示该接口只能消费T类型 interface IMessageCallback<in T> { fun onMessage(data: T) } // 参数改为IMessageCallback<in BaseResp<Any>>,适配子类回调 inline fun send(cb: IMessageCallback<in BaseResp<Any>>?) {} fun test(cb: IMessageCallback<BaseCode>?) { send(cb) // 编译通过,逆变接口支持子类向父类的兼容 } class BaseCode : BaseResp<BaseCode.Data>() { inner class Data { var code = 0 } }
内容的提问来源于stack exchange,提问作者gacmy
相关产品推荐
相关产品推荐

