Как я могу использовать Vararg с различными дженериками в Kotlin? - PullRequest
0 голосов
/ 15 апреля 2019

Я хочу использовать 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 с разными обобщениями, когда мне нужно ограничить типы в vararg?(тип pair.first зависит от типа pair.second)

Ответы [ 2 ]

2 голосов
/ 15 апреля 2019

Если вы хотите сохранить различные функции вместе, вам нужно обработать тип параметра T с out дисперсией. Это означает, что T используется только в выводе класса. На практике это означает, что преобразования Spec<Derived> -> Spec<Base> разрешены, если Derived расширяет / реализует Base.

Без такого ограничения типы функций не связаны, и поэтому вы не можете хранить их в общем массиве (varargs - просто синтаксический сахар для параметров массива).

Пример:

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 также появляется на входе типа функции. Это означает, что вы больше не можете хранить типы функций вместе, потому что они принимают параметры разных типов - с каким типом вы вызываете такую ​​функцию?

Что вам нужно сделать, это найти наиболее распространенный знаменатель. Одним из примеров является принятие параметра Any и выполнение проверки / отправки во время выполнения для фактического типа.

Смотрите также мой недавний ответ здесь: https://stackoverflow.com/a/55572849

0 голосов
/ 15 апреля 2019

Вместо использования 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 если вы не знаете, что означает *.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...