2015-11-04 61 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);}}) –