1

I am using stream to iterate through the list and want to collect it in the map but getting compile time error:

Method reference expression is not expected here

Here is my code

List<Person> personList = getPersons();
Map<String, Integer> personAgeMap = personList.stream()
       .collect(Collectors.toMap(Person::getFirstName + "_" + Person::getLastName, Person::getAge));

I have checked these answers:

but these are not what i am looking for, also i have seen the method reference type.

In this case it is instance method of instance type, how can i have instance of Person in the collectors.

what could be the possible solution or is it even doable this way?

Naman
  • 27,789
  • 26
  • 218
  • 353
Anshul Sharma
  • 1,018
  • 3
  • 17
  • 39
  • 1
    `Person::getFirstName + "_" + Person::getLastName` is invalid syntax I think, you can't concatenate method references to strings – MarcoLucidi Jun 20 '20 at 13:14

3 Answers3

2

You can't do this, it makes no sense:

Collectors.toMap(Person::getFirstName + "_" + Person::getLastName

You need to understand what the signature of the toMap method is. To keep things simple, let's say that it's:

void toMap(Function<? super T,? extends K> keyMapper)

You see? keyMapper is an object of type Function. The function takes an input (a Person) and returns an output (a String). So you need to provide a function which does that. If you write

Person::getFirstName + "_" + Person::getLastName

then that is... well, nothing, but let's say in the worst case it could be a String. Because you're concatenating 3 things with a String. It's not a function. What you want is to create a function.

So you can do this:

Collectors.toMap(new Function<Person, String>() {
   public String apply(Person person) {
      return person.getFirstName() + "_" + person.getLastName();
   }
});

Shorthand for that is:

Collectors.toMap(person -> person.getFirstName() + "_" + person.getLastName()
Csaba
  • 340
  • 1
  • 3
  • 16
1

You can't use a method reference in there. Try a lambda instead.

Collectors.toMap(
    p -> String.format("%s_%s", p.getFirstName(), p.getLastName()), 
    Person::getAge
)

Both Person::getFirstName and Person::getLastName are instances of some functional interface (never of String), for example Supplier<String> or Function<Person, String>, and the operator + can't be applied to them.

Similarly, it wouldn't make much sense, if you were

Object o = new Object() + new Object();

To puzzle you a bit

p -> ((Function<Person, String>) Person::getFirstName).apply(p) + "_" + 
     ((Function<Person, String>) Person::getLastName).apply(p)
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
  • What you are saying is right, but I thought the way we are expecting age from Person::getAge statement same way I was expecting string output from Person::getFirstName and Person::getLastName and then was applying concatenation. Now I understood. – Anshul Sharma Jun 21 '20 at 15:00
1

Method references are used to refere a method in a lambda. It doesn't call the method, it provides its references to the lambda.

Which means that you can't use method reference in the same way as you call the method, so Person::getFirstName + "_" + Person::getLastName isn't right.

So in order to get the result you want, you could just use basic lambda expression like that:

List<Person> personList = getPersons();
Map<String, Integer> personAgeMap = personList.stream()
                                      .collect(Collectors.toMap(person -> person.getFirstName() + "_" + person.getLastName(), Person::getAge));

Hope I got useful.

NoeXWolf
  • 241
  • 1
  • 12