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