【问题标题】:Forcing a terminal operation强制终端操作
【发布时间】:2018-11-19 01:48:16
【问题描述】:

我有一个访问者返回泛型类型以提供使用灵活性:

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

有没有更优雅的方式来强制对流进行终端操作?或者您是否可以提出替代设计来避免该问题?

【问题讨论】:

  • 我认为让visit() 首先返回一个流是错误的。它违反了访问者必须访问对象的期望。返回一个列表,让客户端根据需要进行流式传输。
  • @shmosel 这是一个很好的建议 - 谢谢。我唯一担心的是在通过的过程中会创建很多列表对象。虽然我想我不应该认为这比这个解决方案中创建的所有流更糟糕!我会试一试,但也有兴趣看看是否有其他建议。
  • visitor.visit(this) 返回Optional&lt;T&gt;,你能告诉我你是如何从Optional&lt;T&gt; 生成Stream&lt;T&gt; 的吗? visitor.visit(this).stream(); 是错误的。
  • @Nikolas Optional 从 JDK9 开始具有流方法。查看Optional<T>
  • @Aominè:好吧,我不了解java-9。我添加标签。

标签: java java-stream optional java-9


【解决方案1】:

我会制作两个版本的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。您仍然必须确保在应该使用 visitAll 时不使用 visitAllStream,但这会使错误更加明显。

【讨论】:

    猜你喜欢
    • 2019-08-12
    • 2020-02-01
    • 2020-09-12
    • 2011-09-20
    • 2019-01-14
    • 1970-01-01
    • 1970-01-01
    • 2017-05-24
    • 1970-01-01
    相关资源
    最近更新 更多