Kotlin泛型数字联合?

4
我想为List实现一个累计求和的方法。此函数应接受List、List等类型。我可以说它应该接受任何实现add方法的List类型,但我在官方文档中找不到指定这一点的方法。我尝试使用Number类型,但显然不起作用。如何制作一个泛型扩展函数来接受任何实现特定方法(如add)的类型?

这与Java有什么关系?另外,请勿在标题中标记。 - user202729
3个回答

4

为什么无法添加数字

数字只有以下方法:

  • public abstract fun toDouble(): Double
  • public abstract fun toFloat(): Float
  • public abstract fun toLong(): Long
  • public abstract fun toInt(): Int
  • public abstract fun toChar(): Char
  • public abstract fun toShort(): Short
  • public abstract fun toByte(): Byte

由于没有add方法,因此无法将它们相加。

解决方法

  • 您可以使用Iterable方法,在列表上应该可用
  • 您可以包装数字,并使包装器提供add方法
  • 您可以使用反射来实现,但这可能不是最佳方式

投机取巧的转换解决方法

typealias Adder<T> = (T)->T

fun <T: Number> T.toAdder(): Adder<T> {
    return when(this) {
        is Long -> {{it -> (this as Long + it as Long) as T}}
        is Int -> {{it -> (this as Int + it as Int) as T}}
        is Double -> {{it -> (this as Double + it as Double) as T}}
        else -> throw AssertionError()
    }
}

fun <T: Number> List<T>.mySum(zero: T): T {
    return map { it.toAdder() }.fold(zero) { acc, func -> func(acc)  }
}

fun main(args: Array<String>) {
    val total = listOf(1,2,4).mySum(0)
}

这个方法可以运行,但是需要进行大量的类型转换,应该避免使用


我明白了。那么你就无法对你使用的泛型类型强制执行...合约...,是吗? - Rares Dima
1
你可以强制使用接口和类,但是没有带有add方法的通用接口或类,此外,我的答案是针对普通求和,而不是累积求和。 - jrtapsell

2
在Kotlin中,您可以在stdlib中使用这些扩展函数:
fun Iterable<Byte>.sum(): Int { /* compiled code */ }
fun Iterable<Double>.sum(): Double { /* compiled code */ }
fun Iterable<Float>.sum(): Float { /* compiled code */ }
fun Iterable<Int>.sum(): Int { /* compiled code */ }
fun Iterable<Long>.sum(): Long { /* compiled code */ }
fun Iterable<Short>.sum(): Int { /* compiled code */ }
inline fun <T> Iterable<T>.sumBy(selector: (T) -> Int): Int { /* compiled code */ }
inline fun <T> Iterable<T>.sumByDouble(selector: (T) -> Double): Double { /* compiled code */ }

从中可以看出,没有办法为“具有plus方法的类型列表”编写函数,因为Kotlin不是鸭子类型。

此外,您提到了List<实现add的任何内容>,这是不清楚的(或者说是清楚但不正确的),因为在Kotlin中,所有数字类型都有plus而不是add。由此可以知道,不同的类对“add”操作有自己的定义,并且在不同情况下该操作具有不同的名称。

我建议您使用名为reducereduceRightfoldfoldRight的函数,通过传递参数来自定义您的“add”操作。

例如,List<Int>sum实现基本上是:

fun List<Int>.sum() = fold(0, Int::plus)

等等类似的内容。


你好,我知道sum方法,但累加和是另一回事。累加和是一个列表,其中第i个元素是初始列表的前i个元素之和。例如,1 3 5 2 1的累加和为1 4 9 11 12。 这就是我以这种方式提问的原因。抱歉,但这并没有真正回答我的问题:/ - Rares Dima
天啊。我已经告诉你,没有办法在这些Number的子类型之间重用代码,并以sum的实现为例(证明我所说的是正确的),但你说这并没有回答你的问题。我猜你是在问累加和的实现,这被认为是SO上的垃圾信息。 - ice1000
不,我根本没有要求那个:/ 那只是我在遇到这个问题/问题时处理的算法,所以我用它来举一个具体的例子。我的问题是关于泛型可以做什么和不能做什么的,这可以在问题的最后一行清楚地看到。然而,你的答案更侧重于sum/cumsum的特定情况,而对实际的一般情况问题关注较少。这根本不是垃圾邮件 :) - Rares Dima
在实际的一般情况下问题上远不如此。请看第三个非代码段。 - ice1000

1
在其他语言(Haskell和Scala是最著名的)中,已经有了解决方案,这个解决方案很可能最终会被添加到Kotlin中:类型类。请参阅https://github.com/Kotlin/KEEP/pull/87,了解将它们添加到Kotlin的(非最终)提案。
在它们被添加之前,您可以手动执行类似的操作:
interface Adder<T> {
    fun add(x: T, y: T): T
}

object IntAdder : Adder<Int> {
    fun add(x: Int, y: Int): Int = x + y
}
// similar for other types

// definition of cumulativeSum
fun <T> cumulativeSum(list: List<T>, adder: Adder<T>): List<T> = ...

// call
cumulativeSum(listOf(1,2,3), IntAdder)

类型类解决的问题是您不需要手动传递adder参数,而是编译器会根据T来确定它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接