问题
I am using java 8 stream API to perform action on a list of Store
objects.
Store
takes a String
argument and a Mapper
object. Mapper
will be same for all Store
object.
Question: How can I pass Mapper
object when I initialize Store
here .map(Store::new)
?
public class Store {
public Store(String name, Mapper mapper) {
}
}
public class Mapper {
}
public class Test {
public static void main(String[] args) {
List<String> names = new ArrayList<String>();
Mapper mapper = new Mapper();
// compile time problem at Store::new because it takes 2 arguments
List<Store> actions =
names.stream()
.map(Store::new)
.collect(Collectors.toList());
}
}
回答1:
You can't use a method reference for a constructor that needs to receive a free variable, i.e. a variable from the context.
Please refer to the Java Tutorial, section Method References to find more info about method references.
You can use a lambda expression instead:
Mapper mapper = new Mapper();
List<Store> actions =
names.stream()
.map(name -> new Store(name, mapper))
.collect(Collectors.toList());
If, for whatever reason, you insist on using a method reference, you still can, though the solution is more complex and cumbersome. In fact, it's much better from all possible points of view to use a lambda expression instead of the hack I'm introducing below. I'm writing it just to show that method references are good only if you already have a method or constructor whose signature matches the expected one.
Suppose you declare this helper method:
public static <T, U, R> Function<T, R> bindSecond(
BiFunction<T, U, R> biFunction,
U free) {
return t -> biFunction.apply(t, free);
}
Here I'm are creating and returning a 1-argument function that applies its only one argument to the given bifunction (a 2-argument function), as well as the given free variable. In other words, I'm binding the given free variable to the given bifunction as its second argument.
In your example, Store::new
is actually a bifunction that takes two arguments (name
and mapper
) and returns a value (the new Store
instance), and you are getting that compilation error because Stream.map
expects a 1-argument function that takes the element of the stream as its only one parameter.
The bindSecond
helper method actually transforms the given bifunction and free variable into a 1-argument function that matches the signature of the Stream.map
method.
You could use it as follows:
Mapper mapper = new Mapper();
List<Store> actions =
names.stream()
.map(bindSecond(Store::new, mapper))
.collect(Collectors.toList());
But again, I see no point in using this over a simple lambda expression.
回答2:
Use a lambda expression instead of a method reference
.map(name -> new Store(name, mapper))
来源:https://stackoverflow.com/questions/44144668/how-to-pass-argument-to-class-constructor-when-initialzed-thru-new-in-java8