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

如何实现支持任意可相加类型的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:28:26