Kotlin通用号码联盟?

时间:2018-03-27 11:29:32

标签: generics collections kotlin extension-methods

我想为List实施累积和方法,这样的函数应该接受List<Int>List<Float>等。

我可以说它应该接受List<anything that implements add>

但我认为没有办法在官方文件中指明这一点。

我尝试使用Number类型,但它显然不起作用。

我应该如何制作一个通用扩展函数来接受任何实现add等特定方法的类型?

3 个答案:

答案 0 :(得分: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

没有添加,因此您无法将它们添加到一起

的变通方法

  • 您可以使用Iterable方法,这些方法应该在列表中提供
  • 您可以包装Numbers,并让包装器提供添加方法
  • 您可以使用反射来执行此操作,但这可能不是最佳方式

Hacky强制解决方法

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)
}

这样可行,但它使用了大量的转换,应该避免使用

答案 1 :(得分: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 */ }

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

另外,您提到List<anything that implements add>,其中不清楚(或清楚但不正确),因为在Kotlin中,所有数字类型都有plus而不是add。从中你可以知道,不同的类有自己的“添加”操作的定义,这种操作在不同的情况下有不同的名称。

我建议您使用名为reducereduceRightfoldfoldRight的功能,它允许您通过传递自定义“添加”操作一个论点。

同样,sum List<Int>的实施基本上是:

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

之类的。

答案 2 :(得分: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确定它。