1. 概要

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

この単純な演算子とトランスフォーマーをクラスまたは単純な関数として構築する方法について説明します。

2. Maven構成

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

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

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

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);
                }
            }
        };
    }
}

上記の例では、サブスクライブしているかどうかを確認してから、操作を適用してアイテムを発行する必要があります。

メソッドをチェーンして静的インポートを使用するときに、よりユーザーフレンドリーな読みやすさを実現するために、インスタンスの作成を静的ファクトリメソッドのみに制限しています

そして今、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);
    }
}

トランスフォーマーtoLengthを使用して、オブザーバブルをStringからIntegerの長さに変換することに注意してください。

トランスフォーマーを使用するには、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)はobservableのサブスクライバーで動作しますが、 compose(Transformer)はobservable自体で動作します。

カスタム演算子を作成するとき、オブザーバブル全体を操作する場合はトランスフォーマーを選択し、オブザーバブルによって放出されるアイテムを操作する場合はオペレーターを選択する必要があります。

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("ap_p-l@e", "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演算子の記述方法を示しました。

そして、いつものように、完全なソースコードはGitHubにあります。