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

24,896

Solution 1

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.

This best suited my requirements.

I am new to Kotlin so I don't know how to use Optionals. But from what I understand, I would have to typecast it to User type everytime I need to observe the values right?

Solution 2

As Guenhter explained, this is not possible. However, instead of proposing the null-object pattern, I'd recommend an implementation of the Optional type:

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.

Solution 3

If you use rxkotlin/rxjava 2.0 (I assume so) than the answer is: you can't. The reason is explained here.

This is a break of the interface. Have a look at the Observable Interface

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.

Even if you could, the onNext would immediately throw an error:

@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. e.g. by creating a static object of User which represents your null-element.

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 }

But if is somehow possible, try to avoid the null concept and maybe think of another solution where this isn't needed.

Solution 4

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.

If your app's minSdkVersion less than 24 then you still need to implement it by yourself.

Share:
24,896
Abhinav Nair
Author by

Abhinav Nair

Application Developer and Systems Analyst Working on server-side applications as well as client-side applications - Web front end, Android, iOS. Have worked both full-time and part-time and have had internship opportunities with multiple companies in the past, both startups, and established companies. Have worked as the only developer as well as in teams. Underwent Post Graduate Diploma in Systems Analysis at the National University of Singapore and have completed Bachelor in Technology in India specializing in Computer Science &amp; Engineering. Would like to gain exposure and live experience in projects besides getting exposed to a challenging work environment.

Updated on July 09, 2022

Comments

  • Abhinav Nair
    Abhinav Nair almost 2 years

    I initialize my variable like this:-

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

    But I can't do this. IDE throws an error:-

    user.onNext(null)
    

    And doing this, IDE says u will never be null:-

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