如何在Kotlin中将vararg与不同的泛型一起使用?

时间:2019-04-15 15:38:23

标签: generics kotlin

我想对每个参数具有不同类型的泛型使用vararg

我已经尝试过的方法:

class GeneralSpecification<T> {
    fun <P> ifNotNullCreateSpec(vararg propToCreateFun: Pair<P?, (P) -> Specification<T>>): List<Specification<T>> = 
       propToCreateFun.mapNotNull { (prop, funCreateSpec) ->
            prop?.let(funCreateSpec)
    }
...
}

但是我不能这样使用:

ifNotNullCreateSpec("asdf" to ::createStringSpec, 5 to ::createIntSpec)

(可变参数对中的不同类型)

当我需要限制vararg中的类型时,如何将vararg与不同的泛型一起使用? (pair.first类型取决于pair.second类型)

2 个答案:

答案 0 :(得分:2)

如果要将不同的功能存储在一起,则需要使用T方差来对待参数类型out。这意味着T仅在类的 output 中使用。实际上,这意味着如果Spec<Derived>扩展/实现Spec<Base>,则允许Derived-> Base的转换。

在没有这种约束的情况下,函数类型是不相关的,因此,您不能将它们存储在公共数组中(变量是数组参数的语法糖)。

示例:

class Spec<out T>

fun createStringSpec() = Spec<String>()
fun createIntSpec() = Spec<Int>()

fun <T> ifNotNullCreateSpec(vararg pairs: Pair<T, () -> Spec<T>>) = Unit

fun main() {
    ifNotNullCreateSpec("asdf" to ::createStringSpec, 5 to ::createIntSpec)
}

使用参数T,例如(T) -> Spec<T>T类型也会出现在函数类型的 input 中。这意味着您不能再将函数类型存储在一起,因为它们带有不同类型的参数-您将使用哪种类型的函数?

您在这里需要做的是找到最常见的分母。一个示例是接受Any参数,并对实际类型进行运行时检查/调度。

在此处另请参阅我最近的回答:https://stackoverflow.com/a/55572849

答案 1 :(得分:0)

考虑使用您自己定义的类型,而不是使用Pair

class WithSpec<P, T>(val prop: P?, val funCreateSpec: (P) -> Specification<T>) {
    fun spec() = prop?.let(funCreateSpec)
}

为什么?因为它可以让你做

class GeneralSpecification<T> {
    fun ifNotNullCreateSpec(vararg propToCreateFun: WithSpec<*, T>): List<Specification<T>> = 
       propToCreateFun.mapNotNull { it.spec() }
    ...
}

ifNotNullCreateSpec(WithSpec("asdf", ::createStringSpec), WithSpec(5, ::createIntSpec))

如果您想更接近原始代码,可以轻松添加类似to的扩展函数,并返回WithSpec

如果您不知道*是什么,请参见https://kotlinlang.org/docs/reference/generics.html#star-projections