使用Java 8流的每种元素类型的特定使用者

Gun*_*nar 5 java java-8 java-stream

我有一个Java 8数字流:

Stream<Number> numbers = ...;
Run Code Online (Sandbox Code Playgroud)

我想迭代流并根据每个元素的类型调用特定的使用者.即,对于Integer元素,我想调用Consumer<Integer>,对Long一个Consumer<Long>

forEach()方法,但这需要一个Consumer<? super Number>,要求实现(通常是一个Lambda表达式)instanceof-切换精确类型本身.

就API而言,我基本上是在寻找这样的东西:

numbers.forEach( callbacks -> {
    callbacks.on( Integer.class, i -> { /* handle Integer */ } );
    callbacks.on( Long.class, l -> { /* handle Long */ } )
} );
Run Code Online (Sandbox Code Playgroud)

是否有任何现有的API允许我以类似于此的方式为每个流元素子类型注册特定的消费者?

Mis*_*sha 7

你确定你不想只运行两次流吗?它会更具可读性.

但是如果你愿意,你可以像这样定义一个类型检查用户:

public static<T> Consumer<Object> acceptType(Class<T> clazz, Consumer<? super T> cons) {
    return t -> {
        if (clazz.isInstance(t)) {
            cons.accept(clazz.cast(t));
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用andThen以下方法组合多个

Consumer<Object> combined = acceptType(Integer.class, i -> ...)
                   .andThen(acceptType(Long.class, lng -> ...))
Run Code Online (Sandbox Code Playgroud)

如果要隐藏andThen,可以定义

static<T> Consumer<T> doAll(Consumer<T>... consumers) {
    return Arrays.stream(consumers)
            .reduce(Consumer::andThen)
            .orElse(t -> {});
}
Run Code Online (Sandbox Code Playgroud)

然后它变成了

nums.forEach(doAll(
            acceptType(Integer.class, i -> ...),
            acceptType(Long.class, lng -> ..),
            ...
));
Run Code Online (Sandbox Code Playgroud)

  • 当执行代码块或另一个代码块的决定取决于对象的类时,我立即怀疑设计不好.但是,使用`Consumer.andThen()`的这个构造看起来非常优雅.但是如果仔细观察,你会很快意识到`Consumer.andThen()`链(以及`doAll(消费者)`方法)只不过是一个大的`switch`语句,它为每个元素处理流.正如@LouisWasserman在下面的问题中评论的那样,这应该用访问者模式来实现. (2认同)