简体   繁体   English

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

[英]How to pass null to an Observable with nullable type in RxJava 2 and Kotlin

I initialize my variable like this:-我像这样初始化我的变量:-

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

But I can't do this.但我不能这样做。 IDE throws an error:- IDE 抛出错误:-

user.onNext(null)

And doing this, IDE says u will never be null:-这样做,IDE 说你永远不会为空:-

user.filter( u -> u!=null)

As Guenhter explained, this is not possible.正如Guenhter解释的那样,这是不可能的。 However, instead of proposing the null-object pattern, I'd recommend an implementation of the Optional type:但是,我不建议使用空对象模式,而是建议实现Optional类型:

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

This makes your intent much clearer, and you can use a destructuring declaration in your functions:这使您的意图更加清晰,您可以在函数中使用解构声明:

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

Using the null-object pattern here can cause more bugs than it solves.在这里使用空对象模式可能会导致比它解决的更多的错误。

If you use rxkotlin/rxjava 2.0 (I assume so) than the answer is: you can't.如果您使用 rxkotlin/rxjava 2.0(我认为是),那么答案是:您不能。 The reason is explained here.原因在这里解释。

This is a break of the interface.这是界面的中断。 Have a look at the Observable Interface查看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);
...

The @NonNull will be considered by the Kotlin compiler and therefore you CAN'T pass null. @NonNull将被 Kotlin 编译器考虑,因此您不能传递 null。

Even if you could, the onNext would immediately throw an error:即使可以, 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;
    }
    ...
}

If you really need such a thing as null you have to fake it.如果你真的需要像null这样的东西,你必须伪造它。 eg by creating a static object of User which represents your null -element.例如,通过创建代表您的null元素的User静态对象。

eg例如

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 }

But if is somehow possible, try to avoid the null concept and maybe think of another solution where this isn't needed.但是,如果有可能,请尽量避免使用null概念,并且可能会考虑另一种不需要的解决方案。

Thank you very much for all your answers but I ultimately went with this solution:-非常感谢您的所有回答,但我最终采用了这个解决方案:-

class UserEnvelope(val user:User?) {}

And using this in the observables.并在 observables 中使用它。

This best suited my requirements.这最适合我的要求。

I am new to Kotlin so I don't know how to use Optionals.我是 Kotlin 的新手,所以我不知道如何使用 Optionals。 But from what I understand, I would have to typecast it to User type everytime I need to observe the values right?但据我所知,每次我需要观察值时,我都必须将其类型转换为用户类型,对吗?

To implement the solution mentioned in the nhaarman 's answer, you can use the util class Optional ( doc ) from the Android SDK which was added in API level 24 .要实现nhaarman的答案中提到的解决方案,您可以使用 Android SDK 中的 Util 类Optional ( doc ),该类在API 级别 24 中添加。

If your app's minSdkVersion less than 24 then you still need to implement it by yourself.如果您的应用程序的minSdkVersion小于 24,那么您仍然需要自己实现它。

Since RxJava 2 does not support null values, there are some other acceptable solutions you can use:由于 RxJava 2 不支持值,您可以使用其他一些可接受的解决方案:

  • Work with a custom or third party wrapper library of Optionals like some of the posted answers suggest.使用自定义的或第三方的 Optionals 包装库,就像一些已发布的答案所建议的那样。 When I got rid of Java in favour of Kotlin, Optionals went away in the same package since Kotlin per se supports nullability as part of its type System.当我摆脱 Java 而支持 Kotlin 时,Optionals 在同一个包中消失了,因为 Kotlin 本身支持可空性作为其类型系统的一部分。 Just by this change the code was much more clearer, and I personally don't want to get Optionals back in my code as long as I can avoid them.仅仅通过这个更改,代码就更加清晰了,我个人不想在我的代码中重新使用 Optionals,只要我能避免它们。
  • Emit Any class instances with your subject type.使用您的主题类型发出Any类实例。 For example you could create an Empty.INSTANCE enum class which would emulate the null value and then filter by the enum class.例如,您可以创建一个Empty.INSTANCE枚举类,该类将模拟值,然后按枚举类进行过滤。
  • The last one is the one I use and prefer being a variant of the previous solution and is based on specialisations.最后一个是我使用的,并且更喜欢作为前一个解决方案的变体并且基于专业化。 Our friends of JetBrains always emphasise that classes are very cheap in Kotlin, so this would be a quick example to distinguish logged users and not logged ones:我们 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()) }

I've taken an approach similar to Optional<User> and UserEnvelope .我采用了类似于Optional<User>UserEnvelope I make a simple User class and a ReifiedUser class that inherits from it.我创建了一个简单的User类和一个继承自它的ReifiedUser类。 The User class has a companion object that has a NONE instance. User类有一个带有 NONE 实例的companion object The BehaviorSubject is instantiated with the User.NONE instance. BehaviorSubject是用User.NONE实例实例化的。 It looks something like this:它看起来像这样:

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()

My BehaviorSubject is instantiated like this:我的BehaviorSubject实例化如下:

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

And wherever I need to use activeUser I either flatMap it to Observable.empty() if it's NONE or just figure out what it is and what to do in the subscriber.在我需要使用activeUser任何地方,如果它是 NONE,我要么将它flatMapObservable.empty() ,要么只是弄清楚它是什么以及在订阅者中做什么。

I don't like mixing java Optional with kotlin nullable because mixing map and let gets really confusing and ugly.我不喜欢将 java Optional与 kotlin nullable 混合,因为混合maplet会变得非常混乱和丑陋。 This way it's very obvious what's going on.这样就很清楚发生了什么。

I think it makes more sense to write a container class such as Result .我认为编写一个诸如Result类的容器类更有意义。 An example of that would be一个例子是

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

Usage用法

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM