Kotlin RxJava Nullable Bug

Я столкнулся с проблемой в своем Android-приложении, используя Kotlin и RxJava. Он представлен ниже.

import rx.Observable

data class TestUser(val name: String)

fun getTestUser(): Observable<TestUser> {
    return Observable.just(TestUser("Brian")).flatMap { getUser() } // this compiles
}

fun getTestUser2(): Observable<TestUser> {
    val observable = Observable.just(TestUser("Brian")).flatMap { getUser() }
    return observable // this does not compile
}

fun getUser(): Observable<TestUser?> {
    return Observable.just(null)
}

В getTestUser2 компилятор выводит окончательный тип возврата как Observable<TestUser?> и не компилируется. Однако в getTestUser код компилируется, и когда он запускается, любой абонент к этому наблюдаемому может оказаться неожиданным, когда TestUser возвращается null.

Я предполагаю, что это связано с переходом между Kotlin и Java. Но тот факт, что компилятор видит разницу в getTestUser2, заставляет меня думать, что это может быть исправлено.

Изменить

Это на Kotlin 1.0, окончательная версия выпущена только вчера (15 февраля 2016 г.).

Ответ 1

Подпись функции flatMap следующая, когда используется в Kotlin:

public final fun <R: Any!, T: Any!> 
    Observable<T>.flatMap(
      func: ((T) -> Observable<out R!>!)!
    ) : Observable<R!>!

Из документов:

Любая ссылка в Java может быть null, что делает требования Котлинса строгой нулевой безопасности непрактично для объектов, поступающих с Java. Типы деклараций Java обрабатываются специально в Котлине и называются типы платформ. Нулевые проверки ослаблены для таких типов, так что безопасность гарантии для них такие же, как в Java

и

T! означает "T или T?"

Это означает, что компилятор Kotlin может рассматривать возвращаемый тип функции flatMap как Observable<TestUser> или Observable<TestUser?>, или даже Observable<TestUser>?. Релаксационная часть говорит так: "Мы не хотим беспокоить вас этими неизвестными типами, вы, вероятно, знаете лучше".

Так как тип возврата явно указан в getTestUser(), он использует первый. Поскольку тип observable явно не задан, он передает его Observable<TestUser?> на основе функции getUser().


Как заметил @voddan, есть открытая проблема, обсуждающая эту проблему: https://youtrack.jetbrains.com/issue/KT-11108