Kotlin и RxJava - Почему мой Single.zip() не компилируется?

Я немного сумасшедший. Я пытаюсь создать функцию расширения Observable<BigDecimal> (против RxJava 2.x), чтобы испускать среднее значение выбросов, но я получаю ошибку компиляции с помощью функции Single.zip(). Есть ли у кого-нибудь идеи, что я делаю неправильно? Я попытался быть явным со всеми моими типами, и это не сработало...

import io.reactivex.Observable
import io.reactivex.Single
import java.math.BigDecimal


fun Observable<BigDecimal>.sum() = reduce { total, next -> total + next }

//compile error
fun Observable<BigDecimal>.average() = publish().autoConnect(2).let {
    Single.zip(it.sum().toSingle(), it.count()) {
        sum, count -> sum / BigDecimal.valueOf(count)
    }
}

введите описание изображения здесь

Ответ 1

Вывод типа в основном не работает для rxJava2. Это не проблема типа вывода. Kotlin обычно генерирует методы расширения, заменяющие SAM функциональными типами kotlin, но по какой-то причине эта техника не работает для переопределенных методов.

Подробнее здесь https://youtrack.jetbrains.com/issue/KT-13609

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

fun Observable<BigDecimal>.average() = publish().autoConnect(2).let {
    Single.zip(it.sum().toSingle(), it.count(), BiFunction {
        sum: BigDecimal, count: Long ->
        sum / BigDecimal.valueOf(count)
    })
}

Ответ 2

По какой-то причине вывод типов завершается неудачно, в этом контексте должно быть несколько комбинаций типов.

Вы можете явно указать типы с более традиционным (и, к сожалению, более подробным) синтаксисом, например так:

fun Observable<BigDecimal>.average() = publish().autoConnect(2).let {
    Single.zip(it.sum().toSingle(), it.count(), BiFunction<BigDecimal, Long, BigDecimal> {
        sum, count ->
        sum / BigDecimal.valueOf(count)
    })
}

Обновить:

Я только что выяснил, работая над подобной проблемой, что настоящая проблема заключается в том, что Kotlin не может определить, Single.zip перегрузку Single.zip вы пытаетесь вызвать. Из официальной документации:

Если в классе Java есть несколько методов, принимающих функциональные интерфейсы, вы можете выбрать тот, который вам нужно вызывать, используя функцию адаптера, которая преобразует лямбду в определенный тип SAM. Эти функции адаптера также генерируются компилятором при необходимости.

Таким образом, получается, что использование более явного конструктора SAM решает это само по себе и возвращает вывод типа (в основном мой предыдущий ответ использовал более длинный синтаксис, чем фактически требовалось):

fun Observable<BigDecimal>.average(): Single<BigDecimal> = publish().autoConnect(2).let {
    Single.zip(it.sum().toSingle(), it.count(), BiFunction {
        sum, count ->
        sum / BigDecimal.valueOf(count)
    })
}

Ответ 3

Если вывод типа является проблемой, вы можете использовать RxKotlin

implementation "io.reactivex.rxjava2:rxkotlin:$rxKotlinVersion"

RxKotlin специально предоставляет SAM помощники, чтобы помочь смягчить проблемы с выводом типа.

В таком случае,

Singles.zip(..., ...)

сможет работать просто отлично без какой-либо двусмысленности. Обратите внимание, что я использую Singles а не Single