Несколько Java-потребителей для действия - PullRequest
0 голосов
/ 30 мая 2018

Могу ли я сделать так, чтобы следующее действие фактически было двумя действиями в одном?

static int process(String[] cmd, File dir, Consumer<? super String> action) throws IOException {
    ProcessBuilder pb = new ProcessBuilder(cmd);
    pb.directory(dir);
    pb.redirectErrorStream(true);
    Stopwatch sw = Stopwatch.createStarted();
    Process p = pb.start();
    int exit = -1;
    try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
        br.lines().forEach(action);
    } finally {
        log.debug("Ending process {} with exist code {} in time {}", pb.command(),
                exit = p.destroyForcibly().exitValue(), sw);
    }
    return exit;
}

Так что Consumer<? super String> action Я обычно указываю, что это log::debug или пользовательская функция, такая как AtomicReference::set а что если я хочу быть и тем, и другим?Как я могу заставить действие выполнить несколько независимых действий?

Я знаю, что могу просто создать пользовательский Consumer, который делает все, что я хочу, но я думаю, что было бы очень удобно, если бы я мог почти обработать действиекак эквивалент nvarargs, но для функций / действий.

Ответы [ 3 ]

0 голосов
/ 30 мая 2018

Вы можете сгруппировать несколько потребителей в пределах одного потребителя;с помощью агрегации и передачи сообщения / запроса каждому дочернему потребителю.(и использовать фабрики для обеспечения многих потребителей).

class ManyConsumer<T> implements Consumer<T> {
    List<Consumer<T>> list;
    public ManyConsumer(List<Consumer<T>> list)  {
       this.list=list;
    }
    public void accept(T e) {
        for (Consumer<T> c:list) {  
            c.accept(e);
        }
    }
}
0 голосов
/ 31 мая 2018

Здесь есть несколько альтернатив, некоторые из которых уже упоминались другими.

Возможно, наиболее лямбда-подобным подходом будет использование собственного метода агрегирования andThen (определенного в Consumer):

Stream<X> exes = ....
exes.forEach(action1
    .andThen(action2)
    .andThen(action3)
    .andThen(...)
    .andThen(actionN));

Где все action? объявлены какConsumer<X> или Consumer<? super X>.

Не глядя на документы для подтверждения.Я предполагаю, что действия с 1 по N выполняются одно за другим в одном и том же потоке для каждого элемента в потоке.

Другая возможность здесь - использовать peek, который в основном передает элементы потока в действие, не потребляя их, так как он возвращает поток, который будет содержать те же элементы.

Stream<X> exes = ...
exes.peek(action1)
 .peek(action2)
 .peek(action3)
 .peek(...)
 .forEach(actionN);

Не глядя на документы, держу пари, что:

  • вам нужно действительно вызвать финальное действие, например forEach (или count, empty и т. Д.), Чтобы получитьразные взгляды выполнены.
  • , что единственным ограничением в порядке выполнения является то, что action-i будет идти перед action-j для любого данного элемента в потоке, пока i

Я считаю, что вы можете использовать Consumer.andThen на основеоднако содержание вашего вопроса peek кажется вероятным решением, если есть какое-то действие, выполнение которого не является центральным / основным для поставленной задачи, а скорее как желательный побочный эффект.

Возможно, вы хотите передать измененный поток, который бы "шпионил" за объектами, которые обрабатываются позже кодом, на который была передана ссылка на поток.В этом случае peek является лучшим, если не единственным вариантом.

Конечно, вы также можете сделать комбинацию обоих.

0 голосов
/ 30 мая 2018

Достаточно просто сделать Consumer, который делегирует другим потребителям.Вы просто пишете

 str -> {
   action1(str);
   action2(str);
   // etc
 }

, поскольку у вас нет для использования ссылок на методы, вы всегда можете использовать лямбда-выражения.Написание вашего Consumer в этом стиле также имеет тенденцию быть более читабельным, чем попытка объединить их другими способами, хотя возможны и другие способы.

...