如何实现支持任意可相加类型的List泛型累加和扩展函数?
实现支持任意可累加类型的List泛型扩展函数
问题的核心在于:Kotlin的Number类本身没有定义加法方法,直接用T: Number做泛型约束的话,编译器无法识别子类的加法逻辑。要支持所有具备加法(或自定义add方法)的类型,我们可以用**类型类(Type Class)**模式——这是Kotlin中实现"鸭子类型"约束的常用方案,无需修改现有类的继承结构。
下面是具体实现步骤:
1. 定义类型类接口
首先,我们定义一个接口,规范"可累加类型"必须实现的核心操作:获取累加初始值(零值)、定义两个实例的加法逻辑。
interface SumOperations<T> { // 返回该类型的零值(累加的起始值) fun zero(): T // 定义两个该类型实例的加法规则 fun add(a: T, b: T): T }
2. 为常用数字类型实现类型类
给Int、Float这些内置数字类型提供对应的SumOperations实现:
// Int的累加操作实现 object IntSumOps : SumOperations<Int> { override fun zero(): Int = 0 override fun add(a: Int, b: Int): Int = a + b } // Float的累加操作实现 object FloatSumOps : SumOperations<Float> { override fun zero(): Float = 0.0f override fun add(a: Float, b: Float): Float = a + b } // Double的累加操作实现(可选) object DoubleSumOps : SumOperations<Double> { override fun zero(): Double = 0.0 override fun add(a: Double, b: Double): Double = a + b }
3. 编写List的泛型扩展函数
利用reified泛型(让我们在运行时获取具体类型)和类型判断,自动匹配对应的SumOperations实现,完成累加逻辑:
inline fun <reified T> List<T>.sum(): T { val ops = when (T::class) { Int::class -> IntSumOps as SumOperations<T> Float::class -> FloatSumOps as SumOperations<T> Double::class -> DoubleSumOps as SumOperations<T> // 后续新增支持类型时,在这里添加分支即可 else -> throw IllegalArgumentException("Unsupported type: ${T::class.simpleName}. Please implement SumOperations for this type.") } var total = ops.zero() for (item in this) { total = ops.add(total, item) } return total }
4. 支持自定义类型
如果你的自定义类需要支持累加,只需要两步:
- 定义类的加法逻辑
- 为它实现
SumOperations
示例:
// 自定义Money类 data class Money(val amount: Double) { // 自定义加法方法 fun add(other: Money): Money = Money(this.amount + other.amount) } // 为Money实现SumOperations object MoneySumOps : SumOperations<Money> { override fun zero(): Money = Money(0.0) override fun add(a: Money, b: Money): Money = a.add(b) } // 在sum函数的when分支中添加Money的匹配 inline fun <reified T> List<T>.sum(): T { val ops = when (T::class) { Int::class -> IntSumOps as SumOperations<T> Float::class -> FloatSumOps as SumOperations<T> Double::class -> DoubleSumOps as SumOperations<T> Money::class -> MoneySumOps as SumOperations<T> // 新增该行 else -> throw IllegalArgumentException("Unsupported type: ${T::class.simpleName}. Please implement SumOperations for this type.") } var total = ops.zero() for (item in this) { total = ops.add(total, item) } return total }
5. 使用示例
现在你可以直接调用sum方法,无需额外参数:
val intList = listOf(1, 2, 3, 4) println(intList.sum()) // 输出:10 val floatList = listOf(1.5f, 2.5f, 3.0f) println(floatList.sum()) // 输出:7.0 val moneyList = listOf(Money(10.0), Money(20.5), Money(5.0)) println(moneyList.sum()) // 输出:Money(amount=35.5)
为什么不用Number?
Number是所有数字类型的父类,但它并没有定义加法方法——每个子类(Int、Float等)的加法实现是各自独立的,所以直接用T: Number约束的话,编译器无法确定T是否具备加法能力,自然无法调用。而类型类模式通过外部提供操作实现,完美解决了这个问题。
内容的提问来源于stack exchange,提问作者Rares Dima
相关产品推荐
相关产品推荐

