Introduction to Lambda Expressions
Lambda Type Inference
Multi-threading using Lambda
Functional Interface Annotation
Practical Comparison of Functional Implementation vs Class Type Implementation
So for printAll() and printConditionally() we can integrate into one function which is called as printConditionally() by setting condition to true by default using lambda expression.
This will print all the employees since condition is true.
- Lambda expressions have been recently added in Java 8.
- Lambda expressions enable functional programming.
- Lambda expressions provide readable and concise code.
- Easier to use API's and Libraries.
- Enable support for parallel processing
- Before going through Functional programming let us first recall how we used to perform simple tasks such as Greeting using Object oriented Programming.
- Let us create a new class called as Basics.java we will test lambda expressions in this.
- Inside the class Basics.java we will create a function "greet()" which will return a "Greeting"(String).
- We will do this by passing a behavior in our function.
- This behavior will greet the person on being called.
- Next we will create an interface which will act as our behavior.
- Create an interface called as "Greeting.java".
- This will have a method perform that will greet.
- Next create two classes called as "Hello" and "Finish" that will implement this behavior.
- Hello will output a "Hello" greeting in perform method.
- Finish will output a "Bye" greeting in perform method.
- Next in main method of Basics.java we will call all these
- See the greet method takes in behavior and outputs perform.
- See how in main we have passed different behaviors at different times as needed.
- This is how object Oriented Programming works.
Basics.java
6: package java8.lambda;
11: public class Basics {
12: public void greet(Greeting greet)
13: {
14: greet.perform();
15: }
16: public static void main(String[] args)
17: {
18: Basics basics=new Basics();
19: Hello hello =new Hello();
20: Finish finish=new Finish();
21: basics.greet(hello);
22: System.out.println("We just saw how we can greet a person using Java Behaviours.");
23: basics.greet(finish);
24: }
25: }
Greeting.java
Creating Lambda Expressions
6: package java8.lambda;
11: public interface Greeting {
12: public void perform();
13: }
Hello.java6: package java8.lambda;
11: public class Hello implements Greeting{
12: @Override
13: public void perform() {
14: System.out.println("Hello Everyone");
15: }
16: }
Finish.java6: package java8.lambda;
11: public class Finish implements Greeting{
12: @Override
13: public void perform() {
14: System.out.println("That's all for Now.Have a nice time.Good Bye");
15: }
16: }
Creating Lambda Expressions
- Let us see how the above scenario can be taken up in functional oriented programming using Lambda Expressions.
- Instead of passing a behavior we will pass an action this time in our greet function.
- Inside the function we will then execute the action.
- Lambda expressions are functions which do not belong to a class.
- These functions can be treated as values ,so these methods/functions can be assigned to variables.
- This is only available in Java 8.
- This can be done as follows within a class
- codeblock=() -> {System.out.println("Hello");}
- We have removed the name,return type and access modifier of the function here.
- We don't require a name since the function already has a name.
- We don't require a return type since it is automatically judged by Java compiler.
- We don't need an access modifier since function is publicly available.
- Between the arguments and function we have just added a "->".
- If your function body has just one line of code we can also remove the parenthesis.
- for example
- codeblock=()->System.out.println("Hello World");
- The variable codeblock can now be used anywhere.
- Next we call a greet function and pass in variable code block in it.
- greet(codeblock);
- We can also pass a lambda expression directly to variable.
- greet(() -> System.out.println("Hello World"));
- greetingFunction = () - >System.out.println("Hello World");
- doubleNumberFunction = (int a) - > a *2;
- We don't require a return statement for a single line lambda expression.
- addFunction = (int a,int b) -> a+b;
- safeDividefunction=(int a,int b) ->{if(b==0) return 0; else return a/b;}
- stringLengthCountFunction = (String s) -> s.length();
- If we use lambda expressions as given before directly in our code we will get a type error.
- Each lambda expression must have a type and type is interface which has a look alike method i.e. a method with same return type and parameters
- We have an interface called as Greeting which had a function "perform"
- This function has a void type and no arguments just like our code block.
- Let us define a Lambda Function in our main function in basics.java of type Greeting
Greeting greetingFunction = () -> System.out.println("Hello Lambda");
- If we try to add another method to the interface it will give an error so an interface used for lambda expression must have only one method.
Implementation of Lambda Function as an interface
- We can implement an interface in following ways
- By using Class
- By using an innerClass
- We can also implement an interface using lambda function
- We have seen using Class when we passed
- Greeting hello =new Hello();
- Using Inner class we can implement Interface as follows
Greeting innerClass = new Greeting(){ @Override public void perform() { System.out.println("Hello from Inner Class"); } };
- Using lambda Function we can implement as follows
- Greeting greetingFunction = () -> System.out.println("Hello from Lambda");
- Now we find we can pass all these in our function "greet(Greeting greet)" and this works as well
- What this function is doing is it is calling the perform method of the implementation.
- This works for all these three types of implementations.
- So focusing on lambda function we can implement it directly as an interface with less code rather than implementing it by classes.(See Code below)
Basics.java
public class Basics {
public void greet(Greeting greet)
{
greet.perform();
}
public static void main(String[] args)
{
Basics basics=new Basics();
Greeting hello =new Hello();
Greeting finish=new Finish();
Greeting greetingFunction = () -> System.out.println("Hello from Lambda");
Greeting innerClass = new Greeting(){
@Override
public void perform() {
System.out.println("Hello from Inner Class");
}
};
basics.greet(hello);
basics.greet(greetingFunction);
basics.greet(innerClass);
}
}
Lambda Type Inference
- Java Compiler does Type Inference with Lambda's
- This means it automatically detects the type of parameters and return type of Lambda Expression from the Interface implementation.
- So it will automatically detect the type of data Lambda will deal with.
- Let us create a functionality where we will return the length of String using Lambda.
- We create a new class "MyString".
- We create a new interface "StringLength"
- This interface will have a method which will take a String as parameter and return an integer(length of String)
- Now we will create a Lambda Expression inside my main method which will take a String Object and will return its length.
- StringLength myString= s->s.length();
- Since we have only one parameter we have removed brackets of the parenthesis.
- Parameter "s" is without brackets.
- Also we have not mentioned the type of "s" since java compiler will automatically judge it from the parameter type of function.This is only applicable when we have one parameter.(See code below)
public class MyString {
public static void main(String args[])
{
StringLength myString= s->s.length();
System.out.println(myString.getlength("Lambda"));
getLength(s->s.length());
}
static void getLength(StringLength s)
{
System.out.println(s.getlength("My Lambda"));
}
}
StringLength.java public interface StringLength {
int getlength(String s);
}
Multi-threading using Lambda
- Since Runnable interface has only one method we can create Lambda expressions using the same for multi-threading.
- Let us create a new class called as RunnableExample.
- Create a new Thread called as myThread and pass an anonymous implementation of Runnable interface to the constructor.
- Now instead of using anonymous implementation we can use a Lambda Expression which implements Runnable interface.(See code below)
- Firstly this makes us perform multi-threading using Lambdas
- Secondly the reason why a new type such as "Function Type" was not created for Lambda expressions because it would have caused the need to rewrite all the code again.
- So an existing type called as Interface was used basically to provide backward compatibility for Lambda expression.
RunnableExample.java
public class RunnableExample {
public static void main(String args[])
{
Thread myThread = new Thread(new Runnable() {
public void run() {
System.out.println("Running from Interface Implementation");
}
});
Thread lambdaThread=new Thread(()->System.out.println("Running from Lambda Thread"));
myThread.run();
lambdaThread.run();
}
}
Functional Interface Annotation
- Till now we have seen that an Interface that has one abstract method can be used as a type for Lambda Expression.
- For Example interfaces like Runnable and Greeting which we have used earlier.
- In Java 8 we can have Interfaces which can have implementation methods.
- So an interface which has to be considered as a type for lambda expression must have only one abstract method.
- There can be any number of implementation methods but it has only one abstract method.
- An interface with only one abstract method is called as functional interface.
- A functional interface is a property of the interface.
- Now if we add another abstract method to the functional interface then we cannot use it for lambda expressions.
- Let's try adding a new method in our Greeting interface.
- public void another();
- We see we get error in our implementation classes and Lambda Expressions.
- We can handle implementation classes by adding another method and throwing a "Method not Defined" exception.
- But we can't deal with Lambda Expressions implementing this interface.
- The issue here is we are getting errors in classes and not in interface which may cause problems which changing interface as we may not know which classes are implementing this interface.
- The solution is to add an annotation above the interface "@FunctionalInterface".
- This annotation is from the java.lang library.
- Now whenever we will try to add another abstract method in interface itself it will give an error in interface only.
- The Error is "Unexpected @FunctionalInterface annotation
- Greeting is not a functional interface
- multiple non-overriding abstract methods found in interface Greeting"
@FunctionalInterface
public interface Greeting {
public void perform();
}
Practical Comparison of Functional Implementation vs Class Type Implementation
- Let us create a list of employees and perform some operations on them.
- First we will perform these operations using Object implementation and then we will perform using Functional Implementation.
- Let us create a class called as Employees and a class called as EmployeeRepository.
- Employees will be our entity class and EmployeeRepository will be our class with operations which will also have the main method.
- Inside EmployeeRepository we will create a list of employees.
- We will then perform 3 operations in this class
- Sort Employees by Last Name.
- Create a method that prints all Employees in list.
- Create a method that prints all the employees beginning with "G".
- Inside the Employee class let us have properties first_name,last_name and age.
- Let us also define getter and setter of these properties and define a toString() method which returns all these properties.
- Now inside EmployeeRepository let us take tasks one by one first using class Implementations of Interface type i.e in Java 1.7 and the using Functional Representation of Interface type
- Our first task is to sort employees by last name.
- Since our employees is a type of ArrayList so we will use Collections.sort to perform this operation.
- Since we also have a condition i.e. to sort by Last Name we will use a Comparator interface to add this condition.
- We will use Collections.Sort(Employee list,Comparator<T>) to perform this.
- In this we will implement Compare function of the Comparator Interface to compare 2 objects of the list.
- Next task we need to print employees.
- This is simple we will traverse through the list and print the object which will call the toString() function for output of object details.
- Next we have a function which prints all employees with first name beginning with "G".
- This is a custom condition which cab be implemented in two ways.
- We can have a static function for this condition with one parameter as a List Type of Employee and write code looping through the list and checking if name starts with "G".
- But for this we will have to write a different function for each and every condition.
- Another way is to define an interface with condition and if the condition returns true we will print else we will not.
- Let us create an interface condition with method test which takes argument as list of Employee Type.
- We will implement a custom condition in this method.
- We will then validate this method for printing the Object.
- Now in our EmployeeRepository class let us define a function "printConditionally" with 2 parameters i.e. List of Employee Type and Condtion Interface Type.
- In this function we will traverse through the list and check if the function of Interface returns true.If it returns true then we will print the object.That's all we won't define the condition here.
- In the main method where we will call this method we will provide an implementation of Condition Interface where we will override it's test method with our condition.
- So in this way if we have more than one similar conditions to perform the same action we will not have to write individual functions which is a better approach.
public class Employee {
private String first_name;
private String last_name;
private int age;
public Employee(String first_name, String last_name, int age) {
super();
this.first_name = first_name;
this.last_name = last_name;
this.age = age;
}
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" + "first_name=" + first_name + ", last_name=" + last_name + ", age=" + age + '}';
}
}
EmployeeRepository.java public class EmployeesRepository {
public static void main(String args[])
{
List<Employee> employees=Arrays.asList(
new Employee("Gaurav","Matta",31),
new Employee("Vivek","Sharma",28),
new Employee("Michel","Lee",27),
new Employee("Steve","Warne",45),
new Employee("Ricky","Gichrist",40),
new Employee("Greg","Right",35)
);
//Sort Employees by Last Name
Collections.sort(employees, new Comparator<Employee>(){
@Override
public int compare(Employee o1, Employee o2) {
return o1.getLast_name().compareTo(o2.getLast_name());
}
});
//A method that prints all Employees in List
System.out.println("Printing All Employees :");
printall(employees);
//A method with all the people which have first name beginning with "G"
System.out.println("Printing All Employees First Name Beginning with G:");
printFirstNameBeginningWithG(employees);
//Print with Self Condition(First Name beginning with M)
System.out.println("Printing Conditionally:");
printConditionally(employees,new Condition(){
@Override
public boolean test(Employee e) {
return e.getFirst_name().startsWith("M");
}
});
}
private static void printall(List<Employee> employees) {
for(Employee e : employees)
{
System.out.println(e);
}
}
private static void printFirstNameBeginningWithG(List<Employee> employees) {
for(Employee e : employees)
{
if(e.getFirst_name().startsWith("G"))
{
System.out.println(e);
}
}
}
private static void printConditionally(List<Employee> employees, Condition condition) {
for(Employee e : employees)
{
if(condition.test(e))
{
System.out.println(e);
}
}
}
}
Condition.java public interface Condition {
boolean test(Employee e);
}
- Let us now implement these using Lambda Expressions or Java 8.
- Let us check our first task which is to sort employees by Last Name
- We see that we are using Comparator Interface which is a Functional Interface.
- When we look into the interface we find 2 abstract methods "compare" and "equals".
- int compare(T01,T02)
- boolean equals(Object obj);
- Now we may find this amazing that if there are 2 abstract methods in this interface how come still this interface is marked as Functional Interface.
- This is because Java doc states that if an interface declares an abstract method overriding one of the public method of "java.lang.object", then that does not count towards the interface's abstract method count since any implementation of the interface will have an implementation from java.lang.object or elsewhere.
- Since equals method is a part of object class it does not count towards the interface abstract methods.
- So now we can use a lambda expression within our sort method as follows.
Collections.sort(employees,(e1,e2)->e1.getLast_name().compareTo(e2.getLast_name()));
- The method 3 where we need to implement a method where we want to print all the employees with First name starting with "G" and "M" respectively.
- We can achieve this using implementing Condition Interface as a lambda Function as it has only 1 abstract method.
- We will use the following lambda expressions.
printConditionally(employees,e->e.getFirst_name().startsWith("G")); printConditionally(employees,e->e.getFirst_name().startsWith("M"));
- Now coming to printAll and printConditionally if we can set the condition of Lambda Interface method test to return true it will print the entire list.
- We can do this using Class Implementation too but since here we are talking of functional Implementation we will use the same.What I want to convey is we can use this function instead of printAll(). Let's see how
printConditionally(employees,e->true);
EmployeeRepository.java(updated file)
public class EmployeesRepository {
public static void main(String args[])
{
List<Employee> employees=Arrays.asList(
new Employee("Gaurav","Matta",31),
new Employee("Vivek","Sharma",28),
new Employee("Michel","Lee",27),
new Employee("Steve","Warne",45),
new Employee("Ricky","Gichrist",40),
new Employee("Greg","Right",35)
);
//Sort Employees by Last Name
Collections.sort(employees, new Comparator<Employee>(){
@Override
public int compare(Employee o1, Employee o2) {
return o1.getLast_name().compareTo(o2.getLast_name());
}
});
Collections.sort(employees,(e1,e2)->e1.getFirst_name().compareTo(e2.getFirst_name()));
//A method that prints all Employees in List
System.out.println("Printing All Employees :");
printall(employees);
System.out.println();
System.out.println("Printing All Employees using Lambda Expression :");
printConditionally(employees,e->true);
//A method with all the people which have first name beginning with "G"
System.out.println();
System.out.println("Printing All Employees First Name Beginning with G:");
printFirstNameBeginningWithG(employees);
//Print with Self Condition(First Name beginning with M)
System.out.println();
System.out.println("Printing Conditionally:");
System.out.println("Printing first name with M:");
printConditionally(employees,new Condition(){
@Override
public boolean test(Employee e) {
return e.getFirst_name().startsWith("M");
}
});
System.out.println();
System.out.println("Printing Conditionally using Lambda Expression:");
System.out.println("Printing first name with M using Lambda:");
printConditionally(employees,e->e.getFirst_name().startsWith("G"));
System.out.println("Printing first name with M using Lambda:");
printConditionally(employees,e->e.getFirst_name().startsWith("M"));
}
private static void printall(List<Employee> employees) {
for(Employee e : employees)
{
System.out.println(e);
}
}
private static void printFirstNameBeginningWithG(List<Employee> employees) {
for(Employee e : employees)
{
if(e.getFirst_name().startsWith("G"))
{
System.out.println(e);
}
}
}
private static void printConditionally(List<Employee> employees, Condition condition) {
for(Employee e : employees)
{
if(condition.test(e))
{
System.out.println(e);
}
}
}
}
asd
No comments:
Post a Comment