如何在RxJava 2和Kotlin中将null传递给具有可空类型的Observable

时间:2017-07-25 05:23:02

标签: android kotlin rx-java rx-java2 rx-kotlin

我像这样初始化我的变量: -

abp-ng2-module

但我无法做到这一点。 IDE抛出错误: -

 val user: BehaviorSubject<User?> user = BehaviorSubject.create()

这样做,IDE说你永远不会为空: -

user.onNext(null)

7 个答案:

答案 0 :(得分:11)

正如Guenhter所解释的那样,这是不可能的。但是,我建议实现Optional类型,而不是提出空对象模式:

data class Optional<T>(val value: T?)
fun <T> T?.asOptional() = Optional(this)

这使您的意图更加清晰,您可以在函数中使用解构声明:

Observable.just(Optional("Test"))
  .map { (text: String?) -> text?.substring(1)?.asOptional() }
  .subscribe()

在这里使用空对象模式可能会导致比它解决的更多错误。

答案 1 :(得分:8)

如果您使用rxkotlin / rxjava 2.0(我假设如此),答案是:您不能。原因在这里解释。

这是界面的中断。看看Observable界面

public interface Observer<T> {

    /** ... */
    void onSubscribe(@NonNull Disposable d);

    /** ... */
    void onNext(@NonNull T t);

    /** ... */
    void onError(@NonNull Throwable e);

    /** ... */
    void onSubscribe(@NonNull Disposable d);

    /** ... */
    void onNext(@NonNull T t);

    /** ... */
    void onError(@NonNull Throwable e);
...

Kotlin编译器将考虑@NonNull,因此您不能传递null。

即使你可以,onNext会立即抛出错误:

@Override
public void onNext(T t) {
    if (t == null) {
        onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
        return;
    }
    ...
}

如果你真的需要null这样的东西,你必须伪造它。例如通过创建User的静态对象来表示您的null - 元素。

e.g。

data class User(val username, val password) {

    companion object {
        val NULL_USER = User("", "")
    }
}
...
val user = BehaviorSubject.create<User>()
...
user.onNext(User.NULL_USER)
...
user.filter { it !== User.NULL_USER }

但是在某种程度上可能,尽量避免使用null概念,并考虑另一种不需要的解决方案。

答案 2 :(得分:1)

非常感谢您的所有答案,但我最终选择了这个解决方案: -

{{1}}

在观察者中使用它。

这最符合我的要求。

我是Kotlin的新手所以我不知道如何使用Optionals。但根据我的理解,每次我需要观察值时,我都必须将其类型化为用户类型?

答案 3 :(得分:1)

要实施nhaarman答案中提到的解决方案,您可以使用来自Android SDK的util类Optionaldoc),该类已添加到 API级别24

如果您的应用minSdkVersion小于24,那么您仍然需要自己实现。

答案 4 :(得分:0)

由于RxJava 2不支持 null 值,因此您可以使用其他一些可接受的解决方案:

  • 使用Optionals的自定义或第三方包装器库,例如一些已发布的答案建议。当我摆脱Java而不是Kotlin时,由于Kotlin本身支持可空性作为其类型系统的一部分,因此Optionals在同一个包中消失了。只是通过这种改变,代码更加清晰,我个人不想在我的代码中重新获得Optionals,只要我能避免它们。
  • 使用您的主题类型发出Any类实例。例如,您可以创建一个Empty.INSTANCE枚举类,它将模拟 null 值,然后按枚举类进行过滤。
  • 最后一个是我使用的,并且更喜欢作为先前解决方案的变体,并且基于专业化。我们JetBrains的朋友总是强调在Kotlin中课程非常便宜,所以这将是区分已登录用户和未登录用户的快速示例:

    abstract class SessionUser
    sealed class LoggedUser(val username: String, val password: String) : SessionUser()
    sealed class LogoutUser : SessionUser()
    
    private val user = BehaviorSubject.create<SessionUser>()
    private val loggedUser = 
       user.filter { it is LoggedUser }.cast(LoggedUser::class.java)
    
    fun login(username: String, password: String) {
       user.onNext(LoggedUser(username, password))
    }
    
    fun logout() {
       user.onNext(LogoutUser())
    }
    

答案 5 :(得分:0)

我采用了类似于Optional<User>UserEnvelope的方法。我创建了一个简单的User类和一个从其继承的ReifiedUser类。 User类的companion object类具有NONE实例。 BehaviorSubjectUser.NONE实例实例化。看起来像这样:

open class User {
    companion object {
        val NONE = User()
    }
}

class ReifiedUser(
        @field:JsonProperty(J.FirstName) val firstName: String,
        @field:JsonProperty(J.LastName) val lastName: String
) : User()

我的BehaviorSubject实例化如下:

val activeUser: BehaviorSubject<User> = BehaviorSubject.createDefault(User.NONE)

在需要使用activeUser的任何地方,如果不存在,我可以将其flatMap改为Observable.empty(),或者只是弄清楚它是什么以及在订户中做什么。

我不喜欢将Java Optional与可为空的kotlin混合,因为混合maplet确实会造成混乱和丑陋。这样,很明显发生了什么。

答案 6 :(得分:0)

我认为编写Result这样的容器类更有意义。一个例子是

data class Result<T>(value: T?, error: Throwable?)

用法

Observable.create { observer ->
   upstreamService.listen(object: UpstreamListener {
     onSuccess(data: User) {
       observer.onSuccess(Result(data))
     }
     onError(exception: Throwable) {
       observer.onSuccess(Result(null, exception))
     }
   }
}