在Java流中拆分对象

ytt*_*rrr 4 java java-8 java-stream method-reference

我想知道是否可以在流内拆分对象.例如Employee:

public class Employee {

    String name;
    int age;
    double salary;

    public Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public String getName() { return name; }

    public int getAge() { return age; }

    public double getSalary() { return salary; }
}
Run Code Online (Sandbox Code Playgroud)

我想在流中执行一些操作.为简单起见,让它像这样(假设我的代码架构不允许将它放在Employee类中 - 否则它会太容易了):

public void someOperationWithEmployee(String name, int age, double salary) {
    System.out.format("%s %d %.0f\n", name, age, salary);
}
Run Code Online (Sandbox Code Playgroud)

现在它看起来像这样:

Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
        // some conversations go here ...
        .forEach(e -> someOperationWithEmployee(e.getName, e.getAge(), e.getSalary));
Run Code Online (Sandbox Code Playgroud)

问题是 - 是否可以将一些代码放在流中?

Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
        // some conversations go here
        .forEach((a, b, c) -> someOperationWithEmployee(a, b, c));
Run Code Online (Sandbox Code Playgroud)

我想要实现的目标是什么?- 我想如果我可以映射一些对象字段然后像.forEach(this::someOperationWithEmployee)代码一样处理可读性会略有改善.


2015年5月14日更新

毫无疑问,在这种情况下将Employee对象传递给someOperationWithEmployee最漂亮的解决方案,但有时我们不能在现实生活中做到这一点,并且应该是lambdas的通用解决方案.

Tag*_*eev 5

简短的回答是不,你不能这样做.我能想到的最简单的解决方案是定义你自己的功能界面,如下所示:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

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));
Run Code Online (Sandbox Code Playgroud)

虽然它远非美丽.

我认为如果你someOperationWithEmployeeEmployee对象作为一个参数会更好.

更新:对于这值,您可以使用我的免费StreamEx库,如下所示:

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));
Run Code Online (Sandbox Code Playgroud)

但是它仅限于对,因此您无法以这种方式处理三个或更多值(我不打算添加这些函数).

我还检查了jOOL库,因为它集中在元组上并且已经提供了类似的接口Function3.但是,似乎没有简单的方法可以将它用于您的问题.

  • 你也有http://javaslang.com/ - 在某些方面类似于jOOL. (3认同)
  • 我想最好为你的项目自己创建这样的接口.在我看来,目前这种接口没有"事实上的标准". (2认同)

aal*_*lku 2

我不确定这是否适合您的需求,但它可以进行一些反思,而不是检查某些类型。

您可以这样运行我的解决方案:

    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)));
Run Code Online (Sandbox Code Playgroud)

它将调用“主”类的这个简单方法:

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

当然它需要这个辅助类型:

/** 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);
                        }
                    }
                }
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)