Infinito Nirone 7

白羽の矢を刺すスタイル

RxJava1 から RxJava2 へ移行する時に nullable な値とうまくつきあう

次のような RxJava1 のコードを RxJava2 に移行することを考えます。

import rx.Observable; // RxJava1

private Value nullableValue;

public Observable<Value> observeValue() {
  return Observable.fromEmitter(emitter -> emitter.onNext(nullableValue))
      .filter(nullableValue -> nullableValue != null);
}

Observable のソースとなる値は Nullable で、それを filter で null チェックをかませることで値を受け取る側は NonNull を前提にできるような感じですね。 RxJava2 では onNext() に値を渡す時点で NonNull であることを求められるため、これをそのまま RxJava2 に書き換えると実行時に例外がスローされます。

import io.reactivex.Observable; // RxJava2

private Value nullableValue;

public Observable<Value> observeValue() {
  return Observable.create(emitter -> emitter.onNext(nullableValue)) // nullable な値を onNext には渡せない
      .filter(nullableValue -> nullableValue != null); // 無意味な filter になる
}

ここで、nullableValue を onNext() に渡す時点で Optional*1 を使ってラップしてみると、うまく onNext には NonNull な値を渡せるようになります。 また、その後に filter と map を駆使すると、メソッドの返り値の宣言を変更することなく RxJava2 に移行できます。

import io.reactivex.Observable; // RxJava2

private Value nullableValue;

public Observable<Value> observeValue() {
  return Observable.create(emitter -> emitter.onNext(Optional.of(nullableValue))) // Optional<Value> は NonNull
      .filter(Optional::isPresent) // RxJava1 での null チェックと同じ効果が得られる
      .map(Optional::get); // filter で値が存在することをチェックしているので安全に get できる
}

Android の場合 Optional は GitHub - memoizr/retro-optional: A backport of Java8 optionals for Java7 を使うと古い OS バージョンでも利用できます。

*1:java.util.Optional では ofNullable でラップします。バックポート版ではofでラップします。