«1. Обзор

В этом кратком руководстве мы покажем, как написать собственный оператор с помощью RxJava.

Мы обсудим, как создать этот простой оператор, а также преобразователь — как класс или как простую функцию.

2. Конфигурация Maven

Во-первых, нам нужно убедиться, что у нас есть зависимость rxjava в pom.xml:

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

Мы можем проверить последнюю версию rxjava на Maven Central.

3. Пользовательский оператор

Мы можем создать наш собственный оператор, реализовав интерфейс оператора. В следующем примере мы реализовали простой оператор для удаления небуквенно-цифровых символов из строки:

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

Подъем (оператор) работает с наблюдаемыми подписчиками, но компоновка (трансформатор) работает с наблюдаемыми. сам.

Когда мы создаем наш пользовательский оператор, мы должны выбрать Transformer, если мы хотим работать с наблюдаемым в целом, и выбрать Operator, если мы хотим работать с элементами, испускаемыми наблюдаемым

5. Пользовательский оператор как функция

Мы можем реализовать наш пользовательский оператор как функцию вместо публичного класса:

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.

И, как всегда, полный исходный код можно найти на GitHub.

«