Kotlin泛型接收者扩展函数如何编译期检查参数并避免中间类?
Kotlin实现无中间类的类型安全assertEquals扩展函数
问题背景
我原本针对不同基础类型编写了一系列assertEquals扩展函数:
fun Boolean.assertEquals(value: Boolean) = assertEquals(value, this) fun Int.assertEquals(value: Int) = assertEquals(value, this)
为了简化代码,我将其替换为泛型版本:
fun <T> T.assertEquals(value: T) = assertEquals(value, this)
但这个泛型函数存在缺陷——无法在编译期捕获类型不匹配的错误,例如以下代码能正常编译(不符合预期):
// 编译成功,但类型明显不匹配 "1".assertEquals(1)
后来我通过引入中间类Assertable实现了编译期类型检查:
class Assertable<T>(val value: T) fun <T> Assertable<T>.assertEquals(value: T) = assertEquals(value, this.value) fun <T> T.assert() = Assertable(this)
此时调用"1".assert().assertEquals(1)会直接触发编译错误,符合预期。但我希望找到不需要中间类的实现方式。
解决方案:使用具体化类型参数(reified)
可以通过内联函数+具体化类型参数的组合,在不引入中间类的前提下实现编译期类型检查:
inline fun <reified T> T.assertEquals(value: T) = assertEquals(value, this)
此时再调用"1".assertEquals(1)会直接触发编译错误,因为编译器会将T精确推断为调用者的具体类型(这里是String),而1是Int类型,无法匹配String类型的参数,完美实现了类型安全检查。
原理说明
普通泛型函数的类型参数会被编译器向上推断为所有参数的公共父类型(比如String和Int的公共父类型是Comparable<*> & Serializable),因此类型不匹配的调用也能通过编译。而具体化类型参数借助内联函数的特性,会在编译期将T替换为调用者的具体类型,从而严格限制参数类型必须与调用者完全一致,从根源上避免了跨类型调用。
内容的提问来源于stack exchange,提问作者Boris
相关产品推荐
相关产品推荐

