Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use specific consumer per element type with Java 8 stream

I'm having a Java 8 stream of numbers:

Stream<Number> numbers = ...;

I'd like to iterate the stream and invoke a specific consumer based on the type of each element. I.e. for Integer elements I'd like to invoke a Consumer<Integer>, for Long a Consumer<Long> etc.

There is the forEach() method but this expects a Consumer<? super Number>, requiring that implementation (usually a Lambda expression) to instanceof-switch on the exact type itself.

In terms of an API, I'm essentially looking for something like this:

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

Is there any existing API which would allow me to register a specific consumer per stream element sub-type in a way similar to this?

like image 918
Gunnar Avatar asked Mar 23 '16 13:03

Gunnar


People also ask

What method can you use to apply a given instance of consumer to all the elements of a collection?

The forEach method accepts a Consumer as a parameter. The consumer can be simplified with a lambda expression or a method reference. In the example, we go over the elements of a list with forEach . The consumer simply prints each of the elements.

Can we use forEach in streams Java?

Stream forEach() method in Java with examplesStream forEach(Consumer action) performs an action for each element of the stream. Stream forEach(Consumer action) is a terminal operation i.e, it may traverse the stream to produce a result or a side-effect.

How do you pass a consumer to a method in Java?

java. util. function. Consumer<T> Consumer function type Parameters: T - object type to be passed to the Consumer accept method Consumer function methods: void accept(T t) This method operates on a single object passed in as an argument.


1 Answers

Are you sure you don't want to just run the stream twice? It will be more readable.

But if you want, you can define a type-checking consumer like this:

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

You can then combine multiple consumers using andThen:

Consumer<Object> combined = acceptType(Integer.class, i -> ...)
                   .andThen(acceptType(Long.class, lng -> ...))

If you want to hide andThen, you can define

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

Then it becomes

nums.forEach(doAll(
            acceptType(Integer.class, i -> ...),
            acceptType(Long.class, lng -> ..),
            ...
));
like image 188
Misha Avatar answered Nov 14 '22 22:11

Misha