Handling null in RxJava 2.0
During a migration over to RxJava 2.0 I noticed that a lot of my tests began to fail as they never were getting to “onNext”. After investigating, I noticed in my downstream sometimes my data would become null. In RxJava 2, this now breaks the stream.
After a quick google I found this GitHub issue and discovered that not allowing null is actually a part of the Reactive Streams Spec of which RxJava 2 was written on top of. It is explained in the “What’s different in 2.0" that nulls will now produce an NPE in the downstream.
So what about if you want to use null?
A suggestion in the issue tracker was the use of optionals. Lucky for us Android developers, we recently got support for Optionals. But as per most nice new things, it has a minimum API of 24 which makes it not viable for most of us.
What’s the solution for anything prior to Nougat?
Luckily the actual implementation for Optionals can be really simple:
public class Optional<M> {
private final M optional;
public Optional(@Nullable M optional) {
this.optional = optional;
}
public boolean isEmpty() {
return this.optional == null;
}
public M get() {
if (optional == null) {
throw new NoSuchElementException("No value present");
}
return optional;
}
}
Now we just have to check if the value exists before we get it. Since we now have an object, no mater what, we are guaranteed to continue downstream.
Observable.just(new Optional<>(null))
.subscribe(optional -> {
if (optional.isEmpty()) {
Log.d(TAG, "Object is null");
} else {
Log.d(TAG, "Object value is " + optional.get());
}
});
See, just that simple!
Now because this is an Android Developer post I can’t forget the even cleaner Kotlin implementation
data class Optional<M>(val value : M?)
and to use that class it is as simple as
Observable.just(Optional(null)).subscribe({ optional ->
if (optional.value == null) {
Log.d(TAG, "Object is null")
} else {
Log.d(TAG, "Object value is " + optional.value)
}
})
This is how I chose to get around not allowing null to be passed. Anybody else do something different?
Edit:
So thanks to a helpful user I was pointed out to sealed classes in Kotlin. Another way of doing this is to create your optional class
sealed class Optional<out T> {
class Some<out T>(val element: T): Optional<T>()
object None: Optional<Nothing>()
}
and then in your observable you can split up a when condition based on the type
Observable.just(Optional.Some(1), Optional.None)
.subscribe({ optional ->
when(optional) {
is Optional.Some -> {
Log.d(TAG, "Object value is " + optional.element)
}
is Optional.None -> {
Log.d(TAG, "Object is null")
}
}
})