Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass argument to class constructor when initialzed thru ::new in Java8

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());
    }
}
like image 990
colossal Avatar asked May 23 '17 20:05

colossal


People also ask

How do you pass a parameter to a constructor in Java?

MyClass myClassVar = new MyClass(1975); This example passes one parameter to the MyClass constructor that takes an int as parameter.

When you write a constructor for a class Java will insert a no argument constructor automatically?

Java doesn't require a constructor when we create a class. However, it's important to know what happens under the hood when no constructors are explicitly defined. The compiler automatically provides a public no-argument constructor for any class without constructors. This is called the default constructor.

Can constructor take argument?

A constructor is different from normal functions in following ways: Constructor has same name as the class itself. Default Constructors don't have input argument however, Copy and Parameterized Constructors have input arguments.

What parameters are required to be passed to a class constructor?

This method has four parameters: the loan amount, the interest rate, the future value and the number of periods. The first three are double-precision floating point numbers, and the fourth is an integer.


1 Answers

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.

like image 107
fps Avatar answered Apr 05 '23 17:04

fps