7

Lets say I have a class Gizmo with a constructor that takes a String. Lets say I want to convert a List<String> to a List<Gizmo>.

I might write:

List<String> strings = new ArrayList<>();
List<Gizmo> gizmos = strings
        .stream()
        .map(str -> new Gizmo(str))
        .collect(Collectors.toList());

Now, the problem is that I'm told by IntelliJ that I can replace the lambda with a method reference. Thing is, I'm pretty sure method references can't take parameters.

AaronF
  • 2,841
  • 3
  • 22
  • 32
  • Related: [:: (double colon) operator in Java 8](http://stackoverflow.com/a/22245383/525036) and [What are the uses of constructor reference in java 8](http://stackoverflow.com/questions/29386441/what-are-the-uses-of-constructor-reference-in-java-8) – Didier L May 18 '17 at 08:31
  • 3
    Afaik, if IntelliJ tells you that you can do that, you can tell IntelliJ to do it. Then, you can inspect the result of the refactoring. – Holger May 18 '17 at 08:59

4 Answers4

14

I think InteliJ means replacing

.map(str -> new Gizmo(str))

with

.map(Gizmo::new)

which is a constructor reference. See detailed explanation here.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    And this passes the string into the constructor. I didn't know that. Assumed it was only for empty constructors. Thanks. – AaronF May 18 '17 at 00:51
5

Now, the problem is that I'm told by IntelliJ that I can replace the lambda with a method reference.

it simply means you can change this:

.map(str -> new Gizmo(str))

to this:

.map(Gizmo::new)

you can read more about Constructor method reference.

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
1

There is also a slight optimization when using a method reference instead of a lambda expression also.

Lambda expressions are de-sugared to static/instance method (depends if it's really a lambda/clojure), but method references are not.

In your case when using a lambda expression (t -> new Gizmo(t)) compiler will generate an extra method in your class; that would look like this:

  private static Gizmo lambda$main$0(String s) {
      return new Gizmo(s);   
  }

In case of a method reference (constructor reference) it will not be present.

Eugene
  • 117,005
  • 15
  • 201
  • 306
0

A method reference is just that: a reference to a method, no matter how many arguments the method actually has. A reference to a constructor is just a special case of method reference that references a constructor.

Suppose you have a Person class:

public class Person {

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age  age;
    }

    // getters, setters
}

Now also suppose you have a BiFunction<String, Integer, Person> that maps name and age arguments to instances of the Person class:

BiFunction<String, Integer, Person> personCreator = 
    (String name, Integer age) -> new Person(name, age);

Or, as the types of the parameter of a lambda expression are directly inferred by the compiler:

BiFunction<String, Integer, Person> personCreator = (name, age) -> new Person(name, age);

You can use this 2-arg function as follows:

Person joe = personCreator.apply("Joe", 25);

Now, did you notice that the type and order of the parameters in the (name, age) lambda expression match those in the constructor? This means we might use a method reference instead:

BiFunction<String, Integer, Person> personCreator = Person::new;

And it will work, as expected:

Person jane = personCreator.apply("Jane", 23);

This is just to show you that the number of arguments doesn't matter when using method references. All that needs to match is the signature of the only one abstract method of a functional interface (in this case BiFunction.apply) with the signature of the constructor.

If you want to further read about method references, go to the section about method references in the Java Tutorial.

fps
  • 33,623
  • 8
  • 55
  • 110