import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
public class Testing {
public static void main(String[] args) {
List<B> list = new ArrayList<B>();
B b1=new B(); b1.setName("Durgesh");
B b2=new B(); b2.setName("Val");
list.add(b1);list.add(b2);
MyInterface<B> my1 = B :: printName;
my1.dummyDisplay(b1,b2);
MyInterface<B> my2 = (a,b) -> a.printName(b);
my2.dummyDisplay(b1,b2);
// MyInterface<B> my3 = b1::printName; //compilation error
}
}
class B{
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printName(B b) {
System.out.println(this.name + b.name);
}
}
@FunctionalInterface
interface MyInterface <T> {
public void dummyDisplay(T s, T t);
}
Below lines of code works fine even though printName method of class B accepts only 1 parameter while method dummyDisplay accepts 2 parameters. It is because when we call dummyDisplay ( of functional interface ) method with 2 arguments, compiler uses one argument to invoke printName method and another argument is passed as an argument to printName method. That means (arg1).printName(arg2). Notice the usage of "this" keyword in the method printName.So always remember that in such kinds of method references, number of paramters of the method to be called(printName) should always be 1 less than the number of parameters used in method(dummyDisplay) of functional interface. Such kind of method reference is very frequently used when dealing with POJO classes where we use getters(no arg) of the classes given the functional interface(say Function/Consumer - having methods with 1 parameter).
MyInterface<B> my1 = B :: printName;
my1.dummyDisplay(b1,b2);
I hope you understand this concept.
Now coming to below lines of code. This code is just a replacement of method reference which we discussed just now. Here, since method declared in functional interface has 2 parameters, so we have to and had to use 2 arguments in lambda expression (in this case its a and b). Then a.printName(b) will be written as the definition of interface method (dummyDisplay). Its straight forward. Lambda expression can be used any where provided Functional interfaces(off course).
MyInterface<B> my2 = (a,b) -> a.printName(b);
my2.dummyDisplay(b1,b2);
Now coming to last piece of code. We get compilation error because compiler expects exactly the same number of parameters in printName method of class B which are there in the method of functional interface. Normally this kind of method reference is used just to call any random method of any class which accepts some parameters and does some processing on the accepted data. eg. say add/multiply/divide methods present in the class Calculate or the compare method of Comparator functional interface. In all these cases, method definition does not use "this" keyword. It simply accepts some parameters and performs some task on them. I hope you guys got something out of it.
MyInterface<B> my3 = b1::printName; //compilation error
Having said this, Now lets come to your question,
userList.forEach(User::printName);
works fine because, forEach method internally calls a method accept(arg1) of Consumer interface and your user-defined method printName has no args. So as per my above explanation, it is correct and the compiler does not complaint.
and
userList.forEach(u1::printName);
gives compilation error, because you are using object u1 while referencing instance method printName . So compiler expects same number of parameters of printName method as that there are in accept method of Consumer interface. So it will try to find printName(User param1) from your class User. And since it is not found, compiler complaints about the same.
I hope this helps you guys. Also let me know if I have missed anything or if I have said some thing wrong.
u1::printName? Would you expect it to print "AAA" three times? If not, how do you expectforEachto provide theprintNamemethod with the right user to callprintNameon?forEachto do with each of the users that it's iterating over? (See my answer for more details.)