You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin继承泛型基接口时泛型默认推断为out类型原因咨询

问题根因

不存在“子接口继承基接口时泛型默认被推断为out协变”的规则,你遇到的现象是不变泛型的类型匹配规则、Kotlin数组类型推断的自动投影机制共同导致的:

  • 未加in/out修饰符的泛型是不变型:若A是B的子类型,GenericType<A>和GenericType<B>没有继承关系,无法直接互相赋值。
  • 你定义的FeaturedTestAdapterContract.Presenter继承的是SubPresenter<FeaturedTestAdapterContract.View>,而这个子接口内的View是FeaturedCardAdapterContract.View的子类型,也就是说Presenter的真实类型是SubPresenter<子View>,而非SubPresenter<顶层View>。
  • 当你尝试把两个Presenter实例存入Array<SubPresenter<顶层View>>时,受不变泛型规则限制,SubPresenter<子View>不属于SubPresenter<顶层View>的子类型,直接赋值会触发类型不匹配错误。
  • 此时Kotlin类型推断会自动寻找两个元素的公共父类型做适配,最终匹配到SubPresenter<out 顶层View>:协变投影规则下,SubPresenter<子View>是SubPresenter<out 顶层View>的合法子类型,因此声明数组为out投影或*星投影时可以正常编译。但协变投影本身就禁止调用以泛型类型为入参的方法(避免类型安全漏洞:数组内的Presenter实际只支持绑定的具体子View,无法保证能接受任意顶层View实例),这就是onBind调用失败的直接原因。
问题复现代码
interface FeaturedCardAdapterContract {
    interface View {
        fun onCreate()
    }

    interface SubPresenter<V : View> {
        fun onBind(v: V)
    }
}

interface FeaturedTestAdapterContract {
    interface View : FeaturedCardAdapterContract.View
    interface Presenter : FeaturedCardAdapterContract.SubPresenter<View>
}

fun main() {
    val featureImpl1: FeaturedTestAdapterContract.Presenter = object : FeaturedTestAdapterContract.Presenter {
        override fun onBind(v: FeaturedCardAdapterContract.View) {}
    }
    val featureImpl2: FeaturedTestAdapterContract.Presenter = object : FeaturedTestAdapterContract.Presenter {
        override fun onBind(v: FeaturedCardAdapterContract.View) {}
    }

    // 编译通过:out协变投影可匹配子泛型类型
    val interfaceArray1: Array<FeaturedCardAdapterContract.SubPresenter<out FeaturedCardAdapterContract.View>> = arrayOf(featureImpl1, featureImpl2)

    // 编译失败:不变泛型下SubPresenter<子View>不属于SubPresenter<顶层View>的子类型
    // val interfaceArray2: Array<FeaturedCardAdapterContract.SubPresenter<FeaturedCardAdapterContract.View>> = arrayOf(featureImpl1, featureImpl2)

    // 编译通过:星投影等价于out 上界投影
    val interfaceArray3: Array<FeaturedCardAdapterContract.SubPresenter<*>> = arrayOf(featureImpl1, featureImpl2)

    for (featureImpl in interfaceArray3) {
        // 编译失败:协变/星投影下禁止调用泛型作为入参的方法,避免类型安全问题
        // featureImpl.onBind(object : FeaturedCardAdapterContract.View {
        //     override fun onCreate() {}
        // })
    }
}
修复方案

根据业务场景二选一即可:

  • 如果要求所有Presenter实例都能处理顶层FeaturedCardAdapterContract.View,直接修改子接口的泛型绑定为顶层View:
    interface Presenter : FeaturedCardAdapterContract.SubPresenter<FeaturedCardAdapterContract.View>
    
    修改后Presenter本身就是SubPresenter<顶层View>类型,无需额外投影即可直接存入数组,onBind方法也可正常调用。
  • 如果SubPresenter的泛型参数V只会作为方法入参使用(即Presenter是V的消费者),直接给基接口泛型加上in逆变修饰符:
    interface SubPresenter<in V : View> {
        fun onBind(v: V)
    }
    
    逆变规则下,SubPresenter<子View>天然是SubPresenter<父View>的子类型,无需额外投影即可直接赋值,也不会影响入参方法的正常调用。

内容的提问来源于stack exchange,提问作者Prateek Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 03:39:23