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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33517624/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 14:43:35  来源:igfitidea点击:

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

javalambdajava-8java-stream

提问by user140547

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 Foofrom 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? (without changing Foo, which is a generated file)

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

采纳答案by Holger

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 Foonor its setNamemethod:

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

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


By the way, if fooStringgets 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(","))

回答by aleroot

In this case you don't have too many alternatives unless you add a constructor taking the name as parameter, or you create a static factory methodthat create your instance .

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

回答by Tunaki

No, there is no better way.

不,没有更好的方法。

The only alternative is, like you said in your question, to create a factory for Fooobjects:

唯一的选择是,就像你在你的问题中所说的,为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(",")).

回答by Jaroslaw Pawlak

Another alternative that no one mentioned yet would be to subclass Fooclass, 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);
    }

}

回答by Philipp

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

.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 Foobut of new, anonymous classes which extend 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.

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