Kotlin中如何为lambda类型添加编译时检查?
为Kotlin集合扩展函数实现更严格的泛型类型检查
问题描述
想要实现类似filter、map的集合扩展函数filterIn,但当前泛型约束过于宽松,导致一些不符合类型要求的调用能通过编译,本应在编译阶段就报错。
现有两个filterIn扩展函数:
inline fun <T> Iterable<T>.filterIn(other: Collection<T>): List<T> { return this.filter { it in other } } inline fun <T, R> Iterable<T>.filterIn(other: Collection<R>, transform: (T) -> (R)): List<T> { return this.filter { transform(it) in other } }
以下调用本应编译失败,但实际能正常通过:
// 集合元素类型不匹配,本应编译失败 listOf("1", "2", "3").filterIn(setOf(1, 2)) == emptyList<String>() // Lambda返回类型与集合元素类型不匹配,本应编译失败 listOf("1", "2", "3").filterIn(setOf(1, 2)) { it } == emptyList<String>() // Lambda返回类型与集合元素类型不匹配,本应编译失败 listOf("1", "2", "3").filterIn(setOf(1, 2)) { mapOf("foo" to "bar") } == emptyList<String>()
问题根源
Kotlin编译器会自动推导泛型类型为能让代码合法的最宽泛父类(比如Any),即使实际类型不匹配。例如第一个错误调用中,编译器会将T推导为Any,因为String和Int的共同父类是Any,从而让代码通过编译。
解决方案
通过泛型类型相等约束(Kotlin 1.3+支持),强制要求相关类型必须完全匹配,阻止编译器自动向上推导到公共父类。
修改后的扩展函数
// 无transform参数版本:强制集合元素类型与调用者元素类型完全一致 inline fun <reified T, reified U> Iterable<T>.filterIn(other: Collection<U>): List<T> where T == U { return this.filter { it in other } } // 带transform参数版本:强制transform返回类型与集合元素类型完全一致 inline fun <T, reified R, reified U> Iterable<T>.filterIn(other: Collection<U>, transform: (T) -> R): List<T> where R == U { return this.filter { transform(it) in other } }
效果验证
- 类型不匹配的调用会直接在编译期报错,比如
listOf("1").filterIn(setOf(1))会因为T=String与U=Int不满足T==U而编译失败。 - 合法调用正常通过,比如
listOf(1,2).filterIn(setOf("1","2")) { it.toString() }中,R=String与U=String匹配,可正常编译运行。
内容的提问来源于stack exchange,提问作者Claudio
相关产品推荐
相关产品推荐

