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

Posted

技术标签:

【中文标题】Java 8 - 在 stream.map() 中链接构造函数调用和设置器【英文标题】:Java 8 - chaining constructor call and setter in stream.map() 【发布时间】:2016-02-04 16:44:50 【问题描述】:

我有课

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,这是一个生成的文件)

【问题讨论】:

如果流中只有 zip... 【参考方案1】:

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

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

【讨论】:

亲爱的 holger,你能给我一个链接到一个很好的教程来学习使用这个技能吗?这是一种模式吗?这种做法有名字吗??【参考方案2】:

不,没有更好的办法。

正如您在问题中所说,唯一的选择是为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】:

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

【讨论】:

【参考方案4】:

.map(n -&gt; new Foo() name = n; )

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

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

【讨论】:

...或:.map(n -> new Foo() setName(n); )【参考方案5】:

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

public class Bar extends Foo 

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


【讨论】:

以上是关于Java 8 - 在 stream.map() 中链接构造函数调用和设置器的主要内容,如果未能解决你的问题,请参考以下文章

Java 8 中的 map() 和 flatMap() 方法有啥区别?

Java8之Stream/Map

通俗易懂,java8 .stream().map().collect()用法

通俗易懂,java8 .stream().map().collect()用法

通俗易懂,java8 .stream().map().collect()用法

通俗易懂,java8 .stream().map().collect()用法