在Java8流中使用每个元素类型的特定使用者

在Java8流中使用每个元素类型的特定使用者,java,java-8,java-stream,Java,Java 8,Java Stream,我有一个Java 8数字流: Stream<Number> numbers = ...; 是否有任何现有API允许我以类似于此的方式为每个流元素子类型注册特定的使用者?您确定不想只运行流两次吗?它将更具可读性 但如果需要,可以定义如下类型检查使用者: numbers.forEach( callbacks -> { callbacks.on( Integer.class, i -> { /* handle Integer */ } ); callbacks

我有一个Java 8数字流:

Stream<Number> numbers = ...;

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

您确定不想只运行流两次吗?它将更具可读性

但如果需要,可以定义如下类型检查使用者:

numbers.forEach( callbacks -> {
    callbacks.on( Integer.class, i -> { /* handle Integer */ } );
    callbacks.on( Long.class, l -> { /* handle Long */ } )
} );
public static<T> Consumer<Object> acceptType(Class<T> clazz, Consumer<? super T> cons) {
    return t -> {
        if (clazz.isInstance(t)) {
            cons.accept(clazz.cast(t));
        }
    };
}
如果要隐藏
然后
,可以定义

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

我不知道这样的内置功能,所以这里有一个选项:在
映射中收集所有
消费者
,并按其消费的类型键入,然后查找正确的消费者。如果您使用很多类型,并且这些类型可以动态更改,我建议您使用此解决方案。最有效的解决方案(按cpu消耗量)可能是在
item.getType()
上使用
开关

公共类强制转换{
公共静态void main(字符串[]args){
Stream=Arrays.Stream(新编号[]{3,4L});

MapI不知道有任何现有的库可以做到这一点,但根据您所做的API草图,它看起来相当容易实现。作为一种解决方法,您可以编写一个回调(可能带有内置工厂),将工作分发给相应的工作人员。@biziclop,是的,实现起来并不困难(这是我开始做的,但后来我开始怀疑我是否在这里重新发明轮子)。您可以编写这样一个API。通常,您会使用多态性并在所有实例上调用相同的方法。我强烈建议您使用您的原始设计-应该构建一个以这种方式使用的类层次结构来处理访问者模式。返回t->Optional.of(t).filter的可读性问题是什么(clazz::isInstance).map(clazz::cast).ifPresent(使用者)在acceptType?中,它会在
null
值上爆炸,这可能是OP想要的,也可能不是OP想要的。除此之外,这是个人偏好的问题。当执行代码块或另一个代码块的决定取决于对象的类时,我立即怀疑是一个糟糕的设计。
Consumer.and()的构造
看起来非常优雅。但是如果你仔细观察,你会很快意识到
消费者。然后()
链(以及
doAll(消费者)
方法)只不过是为流的每个元素处理的一个大的
switch
语句。正如@LouisWasserman在下面的问题中所评论的,这应该通过访问者模式来实现。谢谢,回答得很好。
然后()
chain是我一直在寻找的。我不同意其他评论员的说法,即在没有双重分派/visitor的情况下解决这个问题是一种设计的味道。访客增加了复杂性,并且不是每个需要遍历的层次结构都是在考虑到这一点的情况下设计的。另外,例如,看看
javax.lang.model.type.TypeMirro的层次结构r
,其中提供了一个方法
getKind()
,用于“切换”类型,并允许使用比访问者更简单的代码。本质上,这是Java的一个缺点,用其他语言(如Ceylon)可以更好地解决这一问题。
static<T> Consumer<T> doAll(Consumer<T>... consumers) {
    return Arrays.stream(consumers)
            .reduce(Consumer::andThen)
            .orElse(t -> {});
}
nums.forEach(doAll(
            acceptType(Integer.class, i -> ...),
            acceptType(Long.class, lng -> ..),
            ...
));
public class Casts {
    public static void main(String[] args) {

        Stream<Number> stream = Arrays.stream(new Number[] { 3, 4L });

        Map<Class<?>, Consumer<? super Number>> consumers = new HashMap<>();
        putCastConsumer(consumers, Long.class,
                i -> System.out.println("Mapped long " + i));
        putCastConsumer(consumers, Integer.class,
                i -> System.out.println("Mapped int " + i));

        consumeByType(stream, consumers);
    }

    public static <U, T extends U> void putCastConsumer(
            final Map<Class<?>, Consumer<? super U>> map,
            final Class<T> clazz,
            final Consumer<T> consumer) {
        map.put(clazz, value -> consumer.accept(clazz.cast(value)));
    }

    public static <T> void consumeByType(
            final Stream<T> stream,
            final Map<Class<?>, Consumer<? super T>> consumers) {
        stream.forEach(item -> consumers.get(item.getClass()).accept(item));
    }
}