Принудительная работа терминала - PullRequest
0 голосов
/ 09 июня 2018

У меня есть посетитель, который возвращает универсальный тип для обеспечения гибкости в использовании:

interface Base {
    default <T> Stream<T> accept(Visitor<T> visitor) {
        return visitor.visit(this).stream();
    }
}

class Sub implements Base {
    <T> Stream<T> accept(Visitor<T> visitor) {
        return Stream.concat(super.accept(visitor), visitor.visit(this).stream());
    }
}

interface Visitor<T> {
    default Optional<T> visit(Base base) { 
        return Optional.empty() 
    }

    default Optional<T> visit(Sub sub){ 
        return Optional.empty() 
    }
}

Я создал метод, который посещает поток объектов:

<T> Stream<T> visitAll(Visitor<T> visitor) {
    return getStream().flatMap(o -> o.accept(visitor));
}

Эта работаидеально, когда посетитель возвращает значение:

visitAll(new Visitor<Sub>() {
    Optional<Sub> visit(Sub sub) {
        return Optional.of(sub);
    }
}).forEach(...);

Проблема возникает, когда это используется с посетителем, который не возвращает значение:

visitAll(new Visitor<Void>() {
    Optional<Void> visit(Sub sub) {
        // do something with sub
        return Optional.empty();
    }
});

В этом случае потокне заканчивается, поэтому посещения никогда не происходят.

Возможное решение - принудительно запустить терминальную операцию:

<T> Stream<T> visitAll(Visitor<T> visitor) {
    return getStream()
        .collect(Collectors.toList()).stream()
        .flatMap(o -> o.accept(visitor));
}

Другое решение - всегда использовать значение:

visitAll(new Visitor<Void>() {
    Optional<Void> visit(Sub sub) {
        // do something with sub
        return Optional.empty();
    }
}).findAny();

Существует ли более элегантный способфорсировать терминальную операцию в потоке?Или вы можете предложить альтернативный дизайн, который позволяет избежать этой проблемы?

1 Ответ

0 голосов
/ 11 июня 2018

Я бы сделал две версии visitAll, одну, которая возвращает поток, и другую, которая завершает поток и ничего не возвращает.

<T> Stream<T> visitAllStream(Visitor<T> visitor) {
    return getStream().flatMap(o -> o.accept(visitor));
}

void visitAll(Visitor<?> visitor) {
    getStream().forEach(o -> o.accept(visitor));
}

Таким образом, вы будете использовать visitAllStream только тогда, когда высобираюсь делать дальнейшие операции по результатам.Вам все равно придется убедиться, что вы не используете visitAllStream, когда оно должно быть visitAll, но это сделает ошибки более очевидными.

...