Kotlin泛型Presenter存入特定HashMap的类型兼容问题求助
嘿,我之前也碰到过一模一样的泛型兼容坑,咱们一步步拆解解决它。
首先得搞清楚问题根源:Kotlin泛型默认是不变性的——举个例子,哪怕ConcreteView继承自AbstractView,Presenter<ConcreteView>和Presenter<AbstractView>在编译器眼里也是完全不兼容的类型,直接存入会报错;而强行强转虽然能跑,但编译器没法保证后续操作的类型安全,所以会抛出警告。
下面给你两个类型安全、无警告的解决方案:
方案1:给Presenter接口添加协变修饰符(推荐)
如果Presenter是你自己定义的接口,直接给它的泛型参数加上out关键字声明为协变,就能让子类View的Presenter安全向上转型:
// 调整Presenter接口的泛型为协变 interface Presenter<out V : AbstractView> { // 注意:协变泛型只能作为返回值,不能作为方法入参 fun getAttachedView(): V? } // 此时存储类的put方法可以直接接收任意兼容的Presenter实例 abstract class AbstractPresenterStore { fun put(presenter: Presenter<AbstractView>) { // 直接存入即可,无编译错误也无警告 } }
out关键字相当于告诉编译器:这个泛型参数只会被读取(作为返回值),不会被写入(作为方法入参),所以编译器可以放心允许子类泛型对象赋值给父类泛型引用,完全类型安全。
方案2:在存储类的put方法中使用通配符
如果你没法修改Presenter接口的代码(比如它来自第三方库),可以在存储类的put方法参数里用通配符来兼容所有子类View的Presenter:
abstract class AbstractPresenterStore { // 使用out通配符,允许接收所有Presenter<ConcreteView>这类子类型 fun put(presenter: Presenter<out AbstractView>) { // 直接存入,无警告 } }
这个方案的逻辑和协变一致,通配符out AbstractView表示“任何继承自AbstractView的类型作为Presenter的泛型参数”,编译器会认可这种兼容性,同时保证不会出现类型不安全的操作。
为啥强转会有警告?
当你把Presenter<ConcreteView>强转成Presenter<AbstractView>时,编译器没法确认这个Presenter是否能处理所有AbstractView子类的操作——比如如果Presenter里有个方法需要接收ConcreteView作为入参,后续传入其他AbstractView子类就会触发运行时崩溃。而上面两种方案都是编译器能验证的类型安全操作,所以完全不会有警告。
内容的提问来源于stack exchange,提问作者Michael

