2015-11-04 60 views
25

我有一个类的Java 8 - 链构造函数的调用和setter在stream.map()

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

+0

如果只有流中有zip ... – njzk2

回答

24

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

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的方法:

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(","))可能更有效。

8

在这种情况下,除非添加以名称作为参数的构造函数,否则您没有太多替代方法,或者创建一个static factory method来创建实例。

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(","))

3

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

public class Bar extends Foo { 

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

} 
3

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

它使用一个初始化块来设定一个实例变量。

但是有一个警告:返回的对象实际上不是Foo类型,而是新的匿名类,其扩展Foo。当你遵循Liskov替代原则时,这应该不成问题,但有几种情况可能会成为问题。

+0

...或:.map(n - > new Foo(){{setName(n);}}) –