Java 8 - 在stream.map()中链接构造函数调用和setter

时间:2015-11-04 09:03:56

标签: java lambda java-8 java-stream

我有一个班级

class Foo{
    String name;
    // setter, getter
}

只有一个默认构造函数。

然后,我尝试从某个字符串创建一个Foo列表:

Arrays.stream(fooString.split(","))
            .map(name -> {
                Foo x = new Foo();
                x.setName(name);
                return x;

            }).collect(Collectors.toList()));

由于没有构造函数采用名称,我不能简单地使用方法引用。当然,我可以使用构造函数调用和setter将这三行提取到一个方法中,但有没有更好或简洁的方法呢? (不改变Foo,这是一个生成的文件)

5 个答案:

答案 0 :(得分:26)

如果这种情况反复发生,您可以创建一个通用的实用工具方法来处理给定一个属性值的构造对象的问题:

public static <T,V> Function<V,T> create(
    Supplier<? extends T> constructor, BiConsumer<? super T, ? super V> setter) {
    return v -> {
        T t=constructor.get();
        setter.accept(t, v);
        return t;
    };
}

然后你可以使用它:

List<Foo> l = Arrays.stream(fooString.split(","))
    .map(create(Foo::new, Foo::setName)).collect(Collectors.toList());

请注意,这不是特定于Foo及其setName方法:

List<List<String>> l = Arrays.stream(fooString.split(","))
    .map(create(ArrayList<String>::new, List::add)).collect(Collectors.toList());

顺便说一下,如果fooString变得非常大和/或可能包含大量元素(分割后),使用Pattern.compile(",").splitAsStream(fooString)代替Arrays.stream(fooString.split(","))可能会更有效。< / p>

答案 1 :(得分:11)

不,没有更好的方法。

唯一的选择是,就像您在问题中所说的那样,为Foo个对象创建工厂:

public class FooFactory {
    public static Foo fromName(String name) {
        Foo foo = new Foo();
        foo.setName(name);
        return foo;
    }
}

并像这样使用它:

Arrays.stream(fooString.split(",")).map(FooFactory::fromName).collect(toList());

如果要拆分的名称很多,您可以使用Pattern.compile(",").splitAsStream(fooString)(并将编译的模式存储在常量中以避免重新创建),而不是Arrays.stream(fooString.split(","))

答案 2 :(得分:8)

在这种情况下,除非您添加以名称作为参数的构造函数,否则您没有太多选择,或者您创建了一个用于创建实例的static factory method

答案 3 :(得分:4)

.map(n -> new Foo() {{ name = n; }} )

这使用初始化块来设置实例变量。

但有一点需要注意:返回的对象实际上不是Foo类型,而是扩展Foo的新的匿名类。当你遵循Liskov替换原则时,这应该不是问题,但有一些情况可能会引起关注。

答案 4 :(得分:3)

没有人提到的另一个替代方案是继承Foo类,但是这可能有一些缺点 - 很难说它是否适合解决你的问题,因为我不知道上下文

public class Bar extends Foo {

    public Bar(String name) {
        super.setName(name);
    }

}