RxJavaでのカスタム演算子の実装

1概要

このクイックチュートリアルでは、https://github.com/ReactiveX/RxJava[RxJava]を使用してカスタム演算子を作成する方法を示します。

この単純な演算子とトランスの作成方法について、クラスとしても単純関数としても説明します。

2 Mavenの設定

まず、 pom.xml rxjava 依存関係があることを確認する必要があります。

<dependency>
    <groupId>io.reactivex</groupId>
    <artifactId>rxjava</artifactId>
    <version>1.3.0</version>
</dependency>

Maven Central rxjava の最新バージョンを確認できます。

3カスタムオペレータ

  • Operator interface ** を実装することでカスタム演算子を作成できます。次の例では、削除するための単純な演算子を実装しました。 String の英数字以外の文字:

public class ToCleanString implements Operator<String, String> {

    public static ToCleanString toCleanString() {
        return new ToCleanString();
    }

    private ToCleanString() {
        super();
    }

    @Override
    public Subscriber<? super String> call(final Subscriber<? super String> subscriber) {
        return new Subscriber<String>(subscriber) {
            @Override
            public void onCompleted() {
                if (!subscriber.isUnsubscribed()) {
                    subscriber.onCompleted();
                }
            }

            @Override
            public void onError(Throwable t) {
                if (!subscriber.isUnsubscribed()) {
                    subscriber.onError(t);
                }
            }

            @Override
            public void onNext(String item) {
                if (!subscriber.isUnsubscribed()) {
                    final String result = item.replaceAll("[^A-Za-z0-9]", "");
                    subscriber.onNext(result);
                }
            }
        };
    }
}

上記の例では、操作を適用してアイテムを発行する前に、購読者が購読しているかどうかを確認する必要があります。

  • メソッドをチェーニングして静的インポートを使用する場合には、インスタンスの作成を静的ファクトリメソッドのみに制限して、より読みやすいようにします。

そして今、http://reactivex.io/RxJava/1.x/javadoc/rx/Observable.html#lift(rx.Observable.Operator)[ lift ]演算子を使用して、カスタム演算子を他の演算子と簡単に結び付けることができます。

observable.lift(toCleanString())....

これがカスタムオペレータの簡単なテストです。

@Test
public void whenUseCleanStringOperator__thenSuccess() {
    List<String> list = Arrays.asList("john__1", "tom-3");
    List<String> results = new ArrayList<>();
    Observable<String> observable = Observable
      .from(list)
      .lift(toCleanString());
    observable.subscribe(results::add);

    assertThat(results, notNullValue());
    assertThat(results, hasSize(2));
    assertThat(results, hasItems("john1", "tom3"));
}

4トランス

Transformer インターフェースを実装することで、演算子を作成することもできます。

public class ToLength implements Transformer<String, Integer> {

    public static ToLength toLength() {
        return new ToLength();
    }

    private ToLength() {
        super();
    }

    @Override
    public Observable<Integer> call(Observable<String> source) {
        return source.map(String::length);
    }
}

オブザーバブルオブジェクトを String から Integer の長さに変換するには、トランスフォーマ toLength を使用します。

私たちのトランスフォーマーを使うためにはhttp://reactivex.io/RxJava/1.x/javadoc/rx/Observable.html#compose(rx.Observable.Transformer)[ compose ]演算子が必要です。

observable.compose(toLength())...

これは簡単なテストです。

@Test
public void whenUseToLengthOperator__thenSuccess() {
    List<String> list = Arrays.asList("john", "tom");
    List<Integer> results = new ArrayList<>();
    Observable<Integer> observable = Observable
      .from(list)
      .compose(toLength());
    observable.subscribe(results::add);

    assertThat(results, notNullValue());
    assertThat(results, hasSize(2));
    assertThat(results, hasItems(4, 3));
}

lift(Operator) は監視可能な加入者を操作しますが、__compose(Transformer)は監視可能なもの自体を処理します。

カスタム演算子を作成するときに、オブザーバブル全体を操作する場合は Transformer を選択し、オブザーバブルが放出するアイテムを操作する場合は Operator を選択します

5関数としてのカスタム演算子

カスタム演算子を public class の代わりに関数として実装できます。

Operator<String, String> cleanStringFn = subscriber -> {
    return new Subscriber<String>(subscriber) {
        @Override
        public void onCompleted() {
            if (!subscriber.isUnsubscribed()) {
                subscriber.onCompleted();
            }
        }

        @Override
        public void onError(Throwable t) {
            if (!subscriber.isUnsubscribed()) {
                subscriber.onError(t);
            }
        }

        @Override
        public void onNext(String str) {
            if (!subscriber.isUnsubscribed()) {
                String result = str.replaceAll("[^A-Za-z0-9]", "");
                subscriber.onNext(result);
            }
        }
    };
};

これが簡単なテストです。

List<String> results = new ArrayList<>();
Observable.from(Arrays.asList("[email protected]", "or-an?ge"))
  .lift(cleanStringFn)
  .subscribe(results::add);

assertThat(results, notNullValue());
assertThat(results, hasSize(2));
assertThat(results, hasItems("apple", "orange"));

Transformer の例も同様です。

@Test
public void whenUseFunctionTransformer__thenSuccess() {
    Transformer<String, Integer> toLengthFn = s -> s.map(String::length);

    List<Integer> results = new ArrayList<>();
    Observable.from(Arrays.asList("apple", "orange"))
      .compose(toLengthFn)
      .subscribe(results::add);

    assertThat(results, notNullValue());
    assertThat(results, hasSize(2));
    assertThat(results, hasItems(5, 6));
}

6. 結論

この記事では、RxJava演算子の書き方を示しました。

そして、いつものように、完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/rxjava[over on GitHub]にあります。