我想对每个参数具有不同类型的泛型使用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类型)
答案 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。