Kotlin泛型Lambda类型不匹配:Required: Nothing
Kotlin泛型与Lambda类型不匹配问题解决
错误原因分析
当你将Test实例声明为A<*, *>时,Kotlin编译器会把两个通配符解析为未知的B子类和未知的C子类。where方法要求传入的Lambda必须是(T) -> U类型(其中T、U是当前A实例的具体泛型参数),但通配符的存在让编译器无法确定U的具体类型,只能将其推断为Nothing(Kotlin中所有类型的底层子类型)。而你的Lambda返回的是C类型,自然无法与Nothing匹配,因此抛出类型不匹配错误。
解决方案
方案1:利用协变/逆变优化接口设计
通过in和out关键字声明泛型参数的变化性,让不同泛型参数的A实例可以安全兼容:
// 声明T为逆变(in),U为协变(out) interface A<in T : B, out U : C> { fun where(rdm: (T) -> U): A<T, U> } interface B { fun debugB(): C } interface C class D : B { override fun debugB(): F { return F() } fun toto() {} } class F : C class Test : A<D, F> { override fun where(rdm: (D) -> F): Test { return this } } class Main { init { // 利用协变/逆变特性,将Test实例赋值给A<B, C> val test: A<B, C> = Test() // Lambda参数为B类型,返回C类型,完全匹配方法要求 test.where { q -> q.debugB() } } }
核心逻辑:
in T:表示T是逆变类型,A<D, F>可以安全赋值给A<B, F>(D是B的子类,逆变允许父类泛型接收子类泛型实例)。out U:表示U是协变类型,A<D, F>可以安全赋值给A<D, C>(F是C的子类,协变允许子类泛型实例赋值给父类泛型变量)。- 结合两者,
A<D, F>可直接赋值给A<B, C>,此时where方法接受的Lambda类型正好是(B) -> C,与你的实现完全匹配。
方案2:通过泛型函数捕获通配符类型
如果必须保留A<*, *>的变量声明,可以用泛型函数捕获通配符背后的具体类型,绕过编译器的推断限制:
// 原接口与类保持不变 interface A<T : B, U : C> { fun where(rdm: (T) -> U): A<T, U> } interface B { fun debugB(): C } interface C class D : B { override fun debugB(): F { return F() } fun toto() {} } class F : C class Test : A<D, F> { override fun where(rdm: (D) -> F): Test { return this } } class Main { init { val test: A<*, *> = Test() callWhere(test) } private fun <T : B, U : C> callWhere(a: A<T, U>) { // 强转需保证业务上所有B子类的debugB返回对应U类型实例 a.where { it.debugB() as U } } }
这种方式通过泛型函数callWhere将A<*, *>转换为具体的A<T, U>,让编译器正确识别Lambda类型。注意as U的强转需要业务上保证类型安全,否则会抛出转换异常。
方案3:调整接口方法的参数类型
如果业务对返回类型的精确性要求不高,可以修改where方法的Lambda返回类型为C,直接匹配类型:
interface A<T : B, U : C> { // 将Lambda返回类型改为C,返回的A实例泛型调整为C fun where(rdm: (T) -> C): A<T, C> } class Test : A<D, F> { override fun where(rdm: (D) -> C): A<D, C> { return this } } class Main { init { val test: A<*, *> = Test() test.where { q -> q.debugB() } } }
这种方式牺牲了泛型的精确性,但能快速解决类型匹配问题,适合对返回类型要求宽松的场景。
内容的提问来源于stack exchange,提问作者Basile Lamarque
相关产品推荐
相关产品推荐

