How to pass argument to class constructor on initialization via :: new in Java8

I am using java 8 stream API to perform actions on a list of objects Store

.

Store

takes an argument String

and an object Mapper

. Mapper

will be the same for all objects Store

.

Question: How to pass an object Mapper

on initialization 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());
    }
}

      

+3


source to share


2 answers


You cannot use a method reference for a constructor that needs to get a free variable, that is, a variable from the context.

Refer to the Java Tutorial, Section Method References for more information on 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 any reason you insist on using a method reference, you can still , although the solution is more complex and cumbersome . In fact, it is much better from all possible points of view to use a lambda expression instead of what I describe below. I am writing this simply to show that method references are only good if you already have a method or constructor whose signature matches what is expected.

Suppose you have declared 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 create and return a function with 1 argument that applies only one argument to a given bifurc (function with two arguments) and also a given free variable. In other words, I bind the given free variable to the given bfunction as my second argument.

Your example Store::new

is actually a binary that takes two arguments ( name

and mapper

) and returns a value (new instance Store

), and you get this compilation error because you Stream.map

expect a 1-argument function that takes a stream element as the only parameter.

The helper method bindSecond

actually converts the given bifunction and free variable into a function with one argument that matches the method signature Stream.map

.

You can use it like this:

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 .

+5


source


Use a lambda expression instead of a method reference



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

      

+3


source







All Articles