10

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()); } } 
2
  • 1
    The following might come in handy: stackoverflow.com/questions/25468711/… Another solution would be to use lambda expressions instead of static method references. Commented May 23, 2017 at 20:46
  • To be honest if the mapper is always the same as presented, use a second constructor taking only the String and creating the mapper calling the other constructor: public Store(String name) { this(name, new Mapper()); } Commented Jan 29, 2020 at 18:30

3 Answers 3

15

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.

Sign up to request clarification or add additional context in comments.

Comments

6

Use a lambda expression instead of a method reference

 .map(name -> new Store(name, mapper)) 

1 Comment

You should remove the question mark, as you posted an answer, not a comment…
0

Using Stream generator

Stream.generate(Item::new) .map(item -> new Item("Hello", "Hi")) .limit(10) .forEach(System.out::println);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.