简体   繁体   English

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

[英]Java 8 - chaining constructor call and setter in stream.map()

I have a class 我上课了

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

which just has a default constructor. 它只有一个默认的构造函数。

Then, I am trying to create a List of Foo from some string: 然后,我试图从一些字符串创建一个Foo列表:

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

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

Since there is no constructor which takes a name, I can't simply use a method reference. 由于没有构造函数采用名称,我不能简单地使用方法引用。 Of course, I could extract those three lines, with the constructor call and the setter, into a method but is there any better or concise way to do that? 当然,我可以使用构造函数调用和setter将这三行提取到一个方法中,但有没有更好或简洁的方法呢? (without changing Foo , which is a generated file) (不改变Foo ,这是一个生成的文件)

If this happens repeatedly, you may create a generic utility method handling the problem of constructing an object given one property value: 如果这种情况反复发生,您可以创建一个通用实用程序方法来处理给定一个属性值的构造对象的问题:

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;
    };
}

Then you may use it like: 然后你可以使用它像:

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

Note how this isn't specific to Foo nor its setName method: 注意这不是特定于Foo及其setName方法:

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

By the way, if fooString gets very large and/or may contain lots of elements (after splitting), it might be more efficient to use Pattern.compile(",").splitAsStream(fooString) instead of Arrays.stream(fooString.split(",")) . 顺便说一句,如果fooString变得非常大和/或可能包含很多元素(在拆分之后),使用Pattern.compile(",").splitAsStream(fooString)而不是Arrays.stream(fooString.split(",")) Pattern.compile(",").splitAsStream(fooString)可能更有效Arrays.stream(fooString.split(","))

No, there is no better way. 不,没有更好的方法。

The only alternative is, like you said in your question, to create a factory for Foo objects: 唯一的选择是,就像你在你的问题中所说的那样,为Foo对象创建一个工厂:

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

and use it like this: 并像这样使用它:

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

If there are a lot of names to split, you can use Pattern.compile(",").splitAsStream(fooString) (and store the compiled pattern in a constant to avoid recreation) instead of Arrays.stream(fooString.split(",")) . 如果要拆分很多名称,可以使用Pattern.compile(",").splitAsStream(fooString) (并将编译后的模式存储在常量中以避免重新创建)而不是Arrays.stream(fooString.split(","))

在这种情况下,除非您添加以名称作为参数的构造函数,否则您没有太多选择,或者您创建了一个创建实例的静态工厂方法

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

This uses an initialization block to set an instance-variable. 这使用初始化块来设置实例变量。

There is however a caveat: The returned objects will not actually be of type Foo but of new, anonymous classes which extend Foo . 但是有一个警告:返回的对象实际上不是Foo类型,而是扩展Foo的新的匿名类。 When you follow the Liskov substitution principle this should not be a problem, but there are a few situations where it might be a concern. 当你遵循Liskov替换原则时,这不应该是一个问题,但有一些情况可能是一个问题。

Another alternative that no one mentioned yet would be to subclass Foo class, however this may have some disadvantages - it's difficult to say whether it would be appropriate solution to your problem, as I don't know the context. 另一个没人提到的替代方案是将Foo类子类化,但是这可能有一些缺点 - 很难说它是否适合解决你的问题,因为我不知道上下文。

public class Bar extends Foo {

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

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM