我有一个访问者返回一个通用类型以提供使用灵活性:
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();
是否有更优雅的方法来强制终端操作?或者是否有可以建议的替代设计可以避免这个问题?
答案 0 :(得分:0)
我会创建两个版本的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
时不要使用{{1}},但这会使错误变得更加明显。