Java 8 break forEach с альтернативным действием

У меня есть следующий класс:

public class CloseableRepeater<R extends Closeable> {

    /**
     * Repeats the supplier until the stop condition becomes <code>true</code>.
     *
     * @param supplier The supplier to be repeated.
     * @param until  The stop condition.
     * @param times  The maximum repeat times.
     * @return The result.
     */
    public Optional<R> repeat(Supplier<R> supplier, Predicate<R> until, int times) {
        R r = null;
        for (int i = 0; i < times; i++) {
            r = supplier.get();
            if (until.test(r)) {
                break;
            } else {
                try {
                    r.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
        return Optional.of(r);
    }

}

По сути, он повторяет действие до тех пор, пока условие не будет выполнено, и закрывает элементы, которые его не выполняют.


Мой вопрос

Есть ли возможность переписать метод repeat с помощью функционального программирования?


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

Самое близкое, что у меня есть:

public Optional<R> repeat(Supplier<R> supplier, Predicate<R> until, int times) {
    return IntStream.range(0, times).mapToObj(i -> supplier.get()).filter(until).findFirst();
}

но close часть отсутствует.

Всего 2 ответа


После комментария @ VinceEmigh, я думаю, что нашел решение:

   public Optional<R> functionalRepeat(Supplier<R> supplier, Predicate<R> until, int times) {
        return Stream.generate(supplier).limit(times).filter(until.or(t -> {
            try {
                t.close();
            } catch (IOException e) {
                // Ignore
            }
            return false;
        })).findFirst();
    }

Идея состоит в том, чтобы вызывать close во время фильтрации, связывая предикат while с другим, который выполняет закрытие, используя тот факт, что это короткое замыкание логического ИЛИ.

Условие фильтрации является истинным, если предикат before является истиной, поэтому они логически эквивалентны.

Обновить

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


Цикл не плох и лучше подходит для этой конкретной проблемы. Но вы можете немного почистить это:

public Optional<R> repeat(Supplier<R> supplier, Predicate<R> until, int times) {
    for(int i = 0; i < times; i++) {
        R r = supplier.get();
        if(until.test(r)) return Optional.of(r);
        try {
            r.close();
        } catch(IOException e) {
            // Ignore
        }
    }
    return Optional.empty();
}

Целью метода, безусловно, было возвращение пустого необязательного аргумента, если совпадение не найдено (хотя закрываемое значение не должно поддерживаться и приводить к NullPointerException ). Лучше, когда это обрабатывается внутренним потоком кода, а не предварительной инициализацией переменной с null , что привело к ситуации, когда ваш исходный код не мог отличить null результат, и ни один из существующих элементов и, что еще хуже, не мог вернуть неправильный результат. элемент.

Стоит рассмотреть возможность использования <R extends AutoCloseable> для повышения гибкости, вам нужно только изменить catch(IOException e) для catch(Exception e) пока существующие вызывающие абоненты продолжают работать, но возможны и другие варианты использования.


Есть идеи?

10000