问题
I want to use vararg with generics with different types of each arguments
what I've already tried:
class GeneralSpecification<T> {
fun <P> ifNotNullCreateSpec(vararg propToCreateFun: Pair<P?, (P) -> Specification<T>>): List<Specification<T>> =
propToCreateFun.mapNotNull { (prop, funCreateSpec) ->
prop?.let(funCreateSpec)
}
...
}
but I can't use this like:
ifNotNullCreateSpec("asdf" to ::createStringSpec, 5 to ::createIntSpec)
(different types in vararg pairs)
How can I use vararg with different generics, when I need to restrict types in vararg? (pair.first type depends on pair.second type)
回答1:
Instead of using Pair
, consider defining your own type:
class WithSpec<P, T>(val prop: P?, val funCreateSpec: (P) -> Specification<T>) {
fun spec() = prop?.let(funCreateSpec)
}
Why? Because it allows you to do
class GeneralSpecification<T> {
fun ifNotNullCreateSpec(vararg propToCreateFun: WithSpec<*, T>): List<Specification<T>> =
propToCreateFun.mapNotNull { it.spec() }
...
}
ifNotNullCreateSpec(WithSpec("asdf", ::createStringSpec), WithSpec(5, ::createIntSpec))
You can easily add a to
-like extension function returning WithSpec
if you want to get even closer to your original code.
See https://kotlinlang.org/docs/reference/generics.html#star-projections if you don't know what *
means.
回答2:
If you want to store the different functions together, you need to treat the parameter type T
with out
variance. This means T
is only used in the output of the class. In practice, this means that Conversions of Spec<Derived>
-> Spec<Base>
are allowed, if Derived
extends/implements Base
.
Without such a constraint, the function types are not related, and as such you cannot store them in a common array (varargs are just syntactic sugar for array parameters).
Example:
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)
}
With a parameter T
such as in (T) -> Spec<T>
, the T
type also appears in the input of the function type. This means that you can no longer store the function types together, because they take parameters of different types -- with which type would you invoke such a function?
What you would need to do here is to find the most common denominator. One example is to accept a Any
parameter and do a runtime check/dispatch for the actual type.
See also my recent answer here: https://stackoverflow.com/a/55572849
来源:https://stackoverflow.com/questions/55692725/how-can-i-use-vararg-with-different-generics-in-kotlin