You can use comparator directly in a list for comparing objects. For e.g you have a list of Person and you want to sort it on the last name all you have to do is :
Collections.sort(personList,(p1,p2)->p1.getLastName().comparteTo(p2.getLastName));
or Similarly,
personList.sort((p1,p2)->p1.getLastName().comparteTo(p2.getLastName));
Another example, you want to have a condition that goes through all the person object. For e.g what is you want to print all persons with the last name beginning with a particular letter such as "C" or "D" or in short you want to add a condition that takes an object and test something on it and returns a boolean.
lets implement this requirement first so it will be easy to follow:
Let me explain the concept of Closure with the help of example:
public static void main(String args[]) {
int a = 10;
int b = 20;
doProcess(a, i -> System.out.println(i+b));
}
public static void doProcess(int i, Process p) {
p.process(i);
}
public interface Process{
void process(int i);
}
At the lambda expression, this expression will pass as the body of the implementation of Process and it is pass to the method doProcess, here, the method will accept it and execute it from the body, notice 'b', it is coming from closure, (closure means an act or process of closing something), when there is variable in a scope same as the lambda expression and it is being use (as in here) in lambda expression the JVM will close the changing ability of variable 'b' since the lambda will be pass and can be use at the later point of time till then it is necessary for that variable NOT TO Change its actual value. JVM does that for you and close it for further changes (you can try by assigning another value to 'b').
The This reference:
As you know we cannot access 'this' reference in static syntax, this reference to the object and static is something that is related with class. The this reference is different in terms of lambda as compared to anonymous classes as I have mentioned before lambda expression are not anonymous classes here is one of the proof. when you will try to use the this reference in a class.
When we create an anonymous inner class and use 'this' it will reference to the anonymous inner class not the Parent class. However, if 'this' reference is use in the lambda expression it will point to the object whoever the other code outside the lambda is pointing to. That is, the lambda expression share the same reference with the code in which the lambda expression has been written. As compared to anonymous class it refers to the anonymous class.
Collections.sort(personList,(p1,p2)->p1.getLastName().comparteTo(p2.getLastName));
or Similarly,
personList.sort((p1,p2)->p1.getLastName().comparteTo(p2.getLastName));
Another example, you want to have a condition that goes through all the person object. For e.g what is you want to print all persons with the last name beginning with a particular letter such as "C" or "D" or in short you want to add a condition that takes an object and test something on it and returns a boolean.
lets implement this requirement first so it will be easy to follow:
public static void printLastNameOnCondition(List<Person> people) {
for(Person p : people) {
if(p.getLastName().startsWith("C")){
System.out.println(p.toString());
}
if(p.getAge()==30){
System.out.println(p.toString());
}
}
}
Obviously, this looks strange you have to add more "if" blocks in order to insert another condition. What if we create an interface of Condition and provide an abstract method of test and the implementation will be a lambda expression. Like this:
@FunctionalInterface
public interface Condition{
public boolean test(Person p);
}
public static void printLastNameOnCondition(List<Person> people, Condition condition) {
for(Person p : people) {
if(condition.test(p)) {
System.out.println(p.toString());
}
}
}
public static void main(String args[]) {
List<Person> people = getThePersonListFromSomeWhere();
printLastNameOnCondtion(people, p->p.getLastName().startsWith("C"));
printLastNameOnCondtion(people, p->p.getAge()==30);
printLastNameOnCondtion(people, p->true);//print all person objects
}
These type of common patterns of test on condition has also been entertained as funtional interfaces in java.util.function as Predicate. That means you can use the already defined Predicate interface instead of specifying your Condition one. The Predicate has one abstract method with name test. Therefore, using the Predicate will remove the Condition interface and the code becomes like this:
public static void printLastNameOnCondition(List<Person> people, Predicate<Person> condition) {
for(Person p : people) {
if(condition.test(p)) {
System.out.println(p.toString());
}
}
}
public static void main(String args[]) {
List<Person> people = getThePersonListFromSomeWhere();
printLastNameOnCondtion(people, p->p.getLastName().startsWith("C"));
printLastNameOnCondtion(people, p->p.getAge()==30);
printLastNameOnCondtion(people, p->true);//print all person objects
}
The package has lot of predefined interfaces that can be used in such common scenarios, you need to practice them in order to remember.
Some other concepts around lambda:
When you are using any variable that has been defined in the body of the function which holds the lambda expression and if you try to change the value of the variable it will show a compile time error that the variable must be declared final or it should be effectively final. It means that the JVM will use the value of that variable in lambda expression that can be use at later point of time and the value must be freeze at the first initialization.
Effectively final means you do not have to put intentionally the word final before the variable but if you would not change the variable value again it will be considered as final since the first value has been frozen.
Let me explain the concept of Closure with the help of example:
public static void main(String args[]) {
int a = 10;
int b = 20;
doProcess(a, i -> System.out.println(i+b));
}
public static void doProcess(int i, Process p) {
p.process(i);
}
public interface Process{
void process(int i);
}
At the lambda expression, this expression will pass as the body of the implementation of Process and it is pass to the method doProcess, here, the method will accept it and execute it from the body, notice 'b', it is coming from closure, (closure means an act or process of closing something), when there is variable in a scope same as the lambda expression and it is being use (as in here) in lambda expression the JVM will close the changing ability of variable 'b' since the lambda will be pass and can be use at the later point of time till then it is necessary for that variable NOT TO Change its actual value. JVM does that for you and close it for further changes (you can try by assigning another value to 'b').
The This reference:
As you know we cannot access 'this' reference in static syntax, this reference to the object and static is something that is related with class. The this reference is different in terms of lambda as compared to anonymous classes as I have mentioned before lambda expression are not anonymous classes here is one of the proof. when you will try to use the this reference in a class.
When we create an anonymous inner class and use 'this' it will reference to the anonymous inner class not the Parent class. However, if 'this' reference is use in the lambda expression it will point to the object whoever the other code outside the lambda is pointing to. That is, the lambda expression share the same reference with the code in which the lambda expression has been written. As compared to anonymous class it refers to the anonymous class.
References: javabrains.io