Есть ли способ сохранить типы в параметре vararg?

#android #kotlin #generics #variadic-functions

#Android #kotlin #общие сведения #переменные функции

Вопрос:

Мой вопрос в том, можно ли сделать что-то вроде:

 fun <T, R> someFunction(vararg sources<out T>, doSomething: (vararg sources<out T>) -> R) {
    // do something here
}
  

Итак, если я сделаю что-то вроде:

 someFunction(SomeType<A>(), SomeType<B>(), SomeType<C>()) { a: A, b: B c: C ->
    // do Something
}
  

В принципе, функция более высокого порядка должна требовать все типы параметров.

Причина, по которой я спрашиваю об этом, заключается в том, что я хотел бы упростить код, подобный этому:

 inline fun <T1, T2, R> MediatorLiveData<out R>.merge(source1: LiveData<out T1>, source2: LiveData<out T2>, crossinline merger: (T1?, T2?) -> R?) {
    addSource(source1) {
        this.value = merger.invoke(source1.value, source2.value)
    }
    addSource(source2) {
        this.value = merger.invoke(source1.value, source2.value)
    }
}

inline fun <T1, T2, T3, R> MediatorLiveData<out R>.merge(source1: LiveData<out T1>, source2: LiveData<out T2>, source3: LiveData<out T3>, crossinline merger: (T1?, T2?, T3?) -> R?) {
    addSource(source1) {
        this.value = merger.invoke(source1.value, source2.value, source3.value)
    }
    addSource(source2) {
        this.value = merger.invoke(source1.value, source2.value, source3.value)
    }
    addSource(source3) {
        this.value = merger.invoke(source1.value, source2.value, source3.value)
    }
}

inline fun <T1, T2, T3, T4, R> MediatorLiveData<out R>.merge(source1: LiveData<out T1>, source2: LiveData<out T2>, source3: LiveData<out T3>, source4: LiveData<out T4>, crossinline merger: (T1?, T2?, T3?, T4?) -> R?) {
    addSource(source1) {
        this.value = merger.invoke(source1.value, source2.value, source3.value, source4.value)
    }
    addSource(source2) {
        this.value = merger.invoke(source1.value, source2.value, source3.value, source4.value)
    }
    addSource(source3) {
        this.value = merger.invoke(source1.value, source2.value, source3.value, source4.value)
    }
    addSource(source4) {
        this.value = merger.invoke(source1.value, source2.value, source3.value, source4.value)
    }
}
  

Кто-нибудь может что-нибудь предложить? Заранее спасибо!

Ответ №1:

Попробуйте следующую функцию:

 fun <T, R> someFunction(vararg sources: LiveData<out T>, doSomething: (sources: Array<out LiveData<out T>>) -> R) {
    // ...

    doSomething(sources)
}
  

Кажется, мы не можем использовать модификатор vararg в лямбда-выражении doSomething , замена его на Array сработает.

РЕДАКТИРОВАТЬ: Таким образом, в принципе, вы сможете сделать что-то вроде следующего для разного количества источников без создания дополнительных функций, как в вашем примере:

 fun <T, R> someFunction(vararg sources: LiveData<out T>, doSomething: (sources: Array<out LiveData<out T>>) -> R) {
    sources.forEach {
        addSource(it) {
            doSomething.invoke(sources)
        }
    }
}

// Call someFunction with different number of args:
val l1: LiveData<Int> = MutableLiveData()
val l2: LiveData<String> = MutableLiveData()
val l3: LiveData<String> = MutableLiveData()

// Call with two args:
someFunction(l1, l2) { sources: Array<out LiveData<out Any>> ->
    val data1 = sources[0] as LiveData<Int>
    val data2 = sources[1] as LiveData<String>
    // do your work here   
}
// Or Call with three args:
someFunction(l1, l2, l3) { sources: Array<out LiveData<out Any>> ->
    val data1 = sources[0] as LiveData<Int>
    val data2 = sources[1] as LiveData<String>
    val data3 = sources[2] as LiveData<String>
    // do your work here   
}
  

Комментарии:

1. Я не понимаю, можете ли вы объяснить это дальше? Как это может упростить мои методы, описанные выше?

2. будет ли это работать для разных типов. Нравится someFunction(liveDataOfString, liveDataOfInteger) и это приведет к тому, что someFunction(liveDataOfString, liveDataOfInteger) { someString, someInteger -> // do something here } ?

3. Да, это будет работать для разных типов. Но лямбда-выражение будет выглядеть следующим образом: someFunction(l1, l2, l3) { sources: Array<out LiveData<out Any>> -> ... }

4. Пожалуйста, посмотрите мои правки. Если я правильно понял, вам нужно просто привести его к соответствующему типу, например: ` val data1 = sources[0] как LiveData<Int>`

5. Я тоже так думаю. Я просто надеялся, что может быть какой-то другой способ. В любом случае, я отмечу ваш ответ сейчас. Большое вам спасибо!