Splitting objects inside Java stream

The short answer is no, you cannot do this. The shortest solution I can think up is to define your own functional interface like this:

import java.util.function.Function;

@FunctionalInterface
public interface TriFunction<A,B,C,R> {
    R apply(A a, B b, C c);

    static <I,A,B,C,R> Function<I,R> convert(TriFunction<A,B,C,R> triFn, Function<I,A> aFn, 
                                             Function<I,B> bFn, Function<I,C> cFn) {
        return i -> triFn.apply(aFn.apply(i), bFn.apply(i), cFn.apply(i));
    }
}

And use it like this:

Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
    // some conversations go here
    .forEach(TriFunction.convert((a, b, c) -> someOperationWithEmployee(a, b, c), 
         Employee::getName, Employee::getAge, Employee::getSalary));

Though it's far from being beautiful.

I think it would be much better if your someOperationWithEmployee took the Employee object as an argument.

Update: for the pair of values you may use my free StreamEx library like this:

StreamEx.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
    // some conversations go here
    .mapToEntry(Employee::getName, Employee::getAge)
    .forKeyValue((a, b) -> someOperationWithEmployee(a, b));

However it's limited to pairs only, so you cannot handle three or more values in this way (and I'm not going to add such functions).

I also checked the jOOL library as it's concentrated on tuples and already provides interfaces like Function3. However it seems that there's no easy way to use it for your problem either.


I am not sure this fit your needs but it works with a bit of refection and not checking some types.

You can run my solution this way:

    Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
        .forEach(
                e->ArrayCaller.<TriConsumer<String, Integer, Double>>convert(e::getName, e::getAge, e::getSalary)
                                                                     .call((a, b, c) -> operation(a, b, c)));

It would call this simple method of the 'main' class:

private void operation(String name, int age, double salary) {
    System.out.format("%s %d %.0f\n", name, age, salary);
}

Of course it needs this auxiliary types:

/** Extending interfaces must have a method called consume with N args */
interface NConsumer {}

/*
 * Method must be called consume for reflection.
 *
 * You can define N interfaces like this.
 */
nterface TriConsumer<A, B, C> extends NConsumer {
    void consume(A a, B b, C c);
}

interface ArrayCaller<E extends NConsumer> {
    void call(E code);
    static <T extends NConsumer> ArrayCaller<T> convert(Supplier<?>...argSuppliers) {
        final Object[] args = new Object[argSuppliers.length];
        for (int i = 0; i < argSuppliers.length; i++) {
            args[i] = argSuppliers[i].get();
        }
        return new ArrayCaller<T>() {
            @Override
            public void call(T code) {
                for (Method m: code.getClass().getMethods()) {
                    if (m.getName().equals("consume")) {
                        try {
                            m.invoke(code, args);
                        } catch (IllegalAccessException
                                | IllegalArgumentException
                                | InvocationTargetException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        };
    }
}