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

Kotlin抽象类泛型参数在抽象方法中的类型推断问题咨询

Kotlin协变投影类型参数消费报错问题解答

问题复现

你当前的代码结构如下:

// Bars.kt
abstract class Bar
class BarToo(/* fields */) : Bar()

// Foos.kt
abstract class Foo<T : Bar> {
    abstract fun foo(bar: T)
}

class FooToo : Foo<BarToo>() {
    override fun foo(bar: BarToo) { /* */ }
}

// FoosBars.kt
private val foos = HashMap<String, Foo<out Bar>>()

fun <T : Foo<out Bar>> putFoo(name: String, foo: T) {
    foos.putIfAbsent(name, foo)
}

fun doFoo(name: String, bar: Bar) {
    val foo = foos[name] ?: return

    // 此处报错:Type mismatch: inferred type is Bar but Nothing was expected
    foo.foo(bar)
}

报错原因

  • 你为foos集合的元素声明了Foo<out Bar>的协变投影类型,Kotlin中out协变标记限定了该类型参数只能作为生产者(即只能出现在方法返回值位置),不能作为消费者(出现在方法入参位置)
  • 编译器会自动将协变投影类型中所有消费位置的参数类型替换为Nothing,以此避免类型不匹配的风险:你无法确定从集合中取出的Foo实例实际接收的Bar子类类型,直接传入任意Bar实例很可能出现类型转换错误,Kotlin在编译期就阻止了这种操作

你提到的将泛型声明移到foo方法上的方案确实会丢失类级别泛型的约束能力,子类无法固定foo方法的入参类型,不符合你的设计预期,不推荐使用。

可行实现方案

方案1:手动类型校验+强转(适用于你可以保证传入的bar和对应foo的入参类型匹配的场景)

fun doFoo(name: String, bar: Bar) {
    val foo = foos[name] ?: return
    @Suppress("UNCHECKED_CAST")
    (foo as Foo<Bar>).foo(bar)
}

注意该方案需要你自行保证类型匹配,否则运行时会抛出ClassCastException。

方案2:调整集合声明(适用于所有Foo实例都支持接收任意Bar子类的场景)

如果业务设计上所有Foo实现类都可以处理任意Bar子类,直接修改集合的声明,移除协变投影即可:

private val foos = HashMap<String, Foo<Bar>>()

方案3:封装类型安全校验逻辑(需要编译期类型安全的场景)

在Foo抽象类中新增类型校验方法,调用前先校验再执行,避免运行时异常:

abstract class Foo<T : Bar> {
    abstract fun foo(bar: T)
    abstract fun isSupportedBar(bar: Bar): Boolean
}

class FooToo : Foo<BarToo>() {
    override fun foo(bar: BarToo) { /* */ }
    override fun isSupportedBar(bar: Bar): Boolean = bar is BarToo
}

fun doFoo(name: String, bar: Bar) {
    val foo = foos[name] ?: return
    if (foo.isSupportedBar(bar)) {
        @Suppress("UNCHECKED_CAST")
        (foo as Foo<Bar>).foo(bar)
    }
    // 不匹配的情况可自行加兜底逻辑
}

内容的提问来源于stack exchange,提问作者Ryan Jones

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 08:36:02