Lambda ( λ ) Expression -Java
Lambda
( λ ) Expression
☀ Lambda calculus
is a big change in mathematical world which has been introduced in
1930.
Because of benefits
of Lambda calculus slowly this concepts started using in programming
world.
“LISP” is the
first programming which uses Lambda Expression.
☀ The other
languages which uses lambda expressions are:
C#.Net
C Objective
C
C++
Python
Ruby etc.
and finally in Java
also.
☀ The Main
Objective of Lambda Expression is to bring benefits of functional
programming into
Java.
What is Lambda
Expression (λ):
Lambda Expression is
just an anonymous (nameless) function. That means the function which
doesn’t have the
name, return type and access modifiers.
Lambda Expression
also known as anonymous functions or closures.
Ex: 1
Normal
|
Lambda Expression (λ)
|
public void m1() {
sop(“hello”);
} |
() -> {
sop(“hello”);
} |
| () ->{ sop(“hello”); } | |
| () -> sop(“hello”); | |
public void add(inta, int b) {
sop(a+b);
} |
|
| (inta, int b) ->sop(a+b); | |
public String str(String str) {
return str;
} |
|
| (String str) -> return str; | |
| (str) ->str; |
If the type of the
parameter can be decided by compiler automatically based on the
context then
we can remove types
also.
The above Lambda
expression we can rewrite as (a,b) ->sop (a+b);
Conclusions:
1) A lambda
expression can have zero or more number of parameters (arguments).
Ex:
() ->sop(“hello”);
(int a ) ->sop(a);
(inta, int b) ->
return a+b;
2) Usually we can
specify type of parameter. If the compiler expects the type based on
the context
then we can remove
type. i.e., programmer is not required.
Ex:
(inta, int b) ->
sop(a+b);
(a,b) ->
sop(a+b);
3) If multiple
parameters present then these parameters should be separated with
comma (,).
4) If zero number of
parameters available then we have to use empty parameter [ like ()].
Ex: () ->
sop(“hello”);
5) If only one
parameter is available and if the compiler can expect the type then
we can remove the
type and parenthesis
also.
Ex:
(int a) ->
sop(a);
(a)-> sop(a);
A -> sop(a);
6) Similar to method
body lambda expression body also can contain multiple statements. If
more
than one statements
present then we have to enclose inside within curly braces. If one
statement
present then curly
braces are optional.
7) Once we write
lambda expression we can call that expression just like a method, for
this
functional
interfaces are required.
Functional
Interfaces
If an interface
contain only one abstract method, such type of interfaces are called
functional
interfaces and the
method is called functional method or single abstract method (SAM).
Ex:
Runnable -- It
contains only run() method
Comparable -- It
contains only compareTo() method
ActionListener -- It
contains only actionPerformed()
Callable -- It
contains only call() method
Inside functional
interface in addition to single Abstract method (SAM) we write any
number of
default and static
methods.
Ex:
interface Interf {
public abstract void
m1();
default void m2() {
System.out.println
(“hello”);
}
}
In Java 8, Sun Micro
System introduced @Functional Interface annotation to specify that
the interface is Functional Interface.
Ex:
@Functional
Interface
Interface Interf {
public void m1();
}
This code compiles
without any compilation errors.
Inside Functional
Interface we can take only one abstract method, if we take more than
one abstract
method then compiler
raise an error message that is called we will get compilation error.
Ex:
@Functional
Interface {
public void m1();
public void m2();
}
Inside Functional
Interface we have to take exactly only one abstract method.If we are
not declaring
that abstract method
then compiler gives an error message.
Ex:
@Functional
Interface {
interface Interface
{
}
compilation error
Functional
Interface with respect to Inheritance:
If an interface
extends Functional Interface and child interface doesn’t contain
any abstract method
then child interface
is also Functional Interface
Ex:
@Functional
Interface
interface A {
public void
methodOne();
}
@Functional
Interface
Interface B extends
A {
}
In the child
interface we can define exactly same parent interface abstract
method.
Ex:
@Functional
Interface
interface A {
public void
methodOne();
}
@Functional
Interface
interface B extends
A {
public void
methodOne();
}
No Compile Time
Error
In the child
interface we can’t define any new abstract methods otherwise child
interface won’t be
Functional Interface
and if we are trying to use @Functional Interface annotation then
compiler gives an error message.
@Functional
Interface {
interface A {
public void
methodOne();
}
@Functional
Interface
interface B extends
A {
public void
methodTwo();
}
Compiletime Error
Ex:
@Functional
Interface
interface A {
public void
methodOne();
}
interface B extends
A {
public void
methodTwo();
}
No compile time
error
This’s Normal
interface so that code compiles without error
In the above example
in both parent & child interface we can write any number of
default methods
and there are no
restrictions. Restrictions are applicable only for abstract methods.
Functional
Interface Vs Lambda Expressions:
Once we write Lambda
expressions to invoke it’s functionality, then Functional Interface
is required.We can use Functional Interface reference to refer Lambda
Expression.
Where ever
Functional Interface concept is applicable there we can use Lambda
Expressions
Ex:1
Without Lambda
Expression
interface Interf {
public void
methodOne();
}
public class Demo
implements Interface {
public void
methodOne() {
System.out.println(“method
one execution”);
}
public class Test {
public static void
main(String[] args) {
Interf i = new
Demo();
i.methodOne();
}
}
Above code With
Lambda expression
interface Interf {
public void
methodOne();
}
class Test {
public static void
main(String[] args) {
Interf i = () ->
System.out.println(“MethodOne Execution”);
i.methodOne();
}
}
Without Lambda
Expression
interface Interf {
public void
sum(inta,int b);
}
class Demo
implements Interf {
public void
sum(inta,int b) {
System.out.println(“The
sum:”+(a+b));
}
}
public class Test {
public static void
main(String[] args) {
Interfi = new
Demo();
i.sum(20,5);
}
}
Above code With
Lambda Expression
interface Interf {
public void
sum(inta, int b);
}
class Test {
public static void
main(String[] args) {
Interf i = (a,b)
->System.out.println(“The Sum:” +(a+b));
i.sum(5,10);
}
}
Without Lambda
Expressions
interface Interf {
publicint square(int
x);
}
class Demo
implements Interf {
public int
square(int x) {
return x*x;
}
}
class Test {
public static void
main(String[] args) {
Interf i = new
Demo();
System.out.println(“The
Square of 7 is: “ +i.square(7));
}
}
Above code with
Lambda Expression
interface Interf {
public int
square(int x);
}
class Test {
public static void
main(String[] args) {
Interf i = x ->
x*x;
System.out.println(“The
Square of 5 is:”+i.square(5));
}
}
Without Lambda
expression
class MyRunnable
implements Runnable {
public void run() {
for(int i=0; i<10;
i++) {
System.out.println(“Child
Thread”);
}
}
}
class ThreadDemo {
public static void
main(String[] args) {
Runnable r = new
myRunnable();
Thread t = new
Thread(r);
t.start();
for(int i=0; i<10;
i++) {
System.out.println(“Main
Thread”)
}
}
}
With Lambda
expression
class ThreadDemo {
public static void
main(String[] args) {
Runnable r = () ->{
for(int i=0; i<10;
i++) {
System.out.println(“Child
Thread”);
}
};
Thread t = new
Thread(r);
t.start();
for(i=0; i<10;
i++) {
System.out.println(“Main
Thread”);
}
}
}
Anonymous inner
classes vs Lambda Expressions
Wherever we are
using anonymous inner classes there may be a chance of using Lambda
expression
to reduce length of
the code and to resolve complexity.
Ex: With anonymous
inner class
class Test {
public static void
main(String[] args) {
Thread t = new
Thread(new Runnable() {
public void run() {
for(int i=0; i<10;
i++) {
System.out.println("Child
Thread");
}
}
});
t.start();
for(int i=0; i<10;
i++)
System.out.println("Main
thread");
}
}
With Lambda
expression
class Test {
public static void
main(String[] args) {
Thread t = new
Thread(()->{
for(int i=0; i<10;
i++) {
System.out.println("Child
Thread");
}
});
t.start();
for(int i=0; i<10;
i++) {
System.out.println("Main
Thread");
}
}
}
What are the
advantages of Lambda expression?
We can reduce length
of the code so that readability of the code will be improved.
We can resolve
complexity of anonymous inner classes.
We can provide
Lambda expression in the place of object.
We can pass lambda
expression as argument to methods.
☀ Hence if
anonymous inner class implements Functional Interface in that
particular case only we
can replace with
lambda expressions. Hence wherever anonymous inner class concept is
there, it
may not possible to
replace with Lambda expressions.
Anonymous inner
class can extend concrete class, can extend abstract class, can
implement
interface with any
number of methods
Lambda expression
can implement an interface with only single abstract method
(Functional
Interface).
Inside anonymous
inner class we can declare instance variables.
☀ Anonymous inner
class! = Lambda Expression
☀ Inside anonymous
inner class “this” always refers current inner class
object(anonymous inner
class) but not
related outer class object
Ex:
☀ Inside lambda
expression we can’t declare instance variables.
☀ Whatever the
variables declare inside lambda expression are simply acts as local
variables
☀ Within lambda
expression ‘this” keyword represents current outer class object
reference (that is
current enclosing
class reference in which we declare lambda expression)
Ex:
interface Interf {
public void m1();
}
class Test {
int x = 777;
public void m2() {
Interfi = ()->{
int x = 888;
System.out.println(x);
//888
System.out.println(this.x);
//777
};
i.m1();
}
public static void
main(String[] args) {
Test t = new Test();
t.m2();
}
}
☀ From lambda
expression we can access enclosing class variables and enclosing
method variables
directly.
☀ The local
variables referenced from lambda expression are implicitly final and
hence we can’t
perform
re-assignment for those local variables otherwise we get compile time
error
Ex:
interface Interf {
public void m1();
}
class Test {
int x = 10;
public void m2() {
int y = 20;
Interfi = ()->{
System.out.println(x);
//10
System.out.println(y);
//20
x = 888;
y = 999; //CE
};
i.m1();
y = 777;
}
public static void
main(String[] args) {
Test t = new Test();
t.m2();
}
}
Anonymous Inner Class
|
Lambda Expression
|
Anonymous Inner class
It’s a class without name |
Lambda Expression
It’s a method without name
(anonymous
function) |
Anonymous inner class can extend
Abstract and concrete classes
Anonymous inner class can implement
An interface that contains any
number of
Abstract methods |
lambda expression can’t extend
Abstract and concrete classes
lambda expression can implement an
Interface which contains single
abstract method
(Functional Interface) |
Inside anonymous inner class we can
Declare instance variables. |
Inside lambda expression we can’t
Declare instance variables, whatever
the
variables declared are simply acts
as local
variables. |
Anonymous inner classes can be
Instantiated
Inside anonymous inner class “this”
Always refers current anonymous
Inner class object but not outer
class
Object. |
lambda expressions can’t be
instantiated
Inside lambda expression “this”
Always refers current outer class
object. That is
enclosing class object. |
Anonymous inner class is the best
choice
If we want to handle multiple methods. |
Lambda expression is the best Choice
if we want
to handle interface
With single abstract method
(Functional
Interface). |
In the case of anonymous inner class
At the time of compilation a
separate
Dot class file will be generated
(outerclass$1.class) |
At the time of compilation no dot
Class file will be
generated for Lambda expression. It
simply
converts in to private method outer class. |
Memory allocated on demand
Whenever we are creating an object |
Reside in permanent memory of JVM |
Lambda
Expressions with Collections
Collection is
nothing but a group of objects represented as a single entity.
The important
Collection types are:
1. List(I)
2. Set(I)
3. Map(I)
1. List(I):
If we want to
represent a group of objects as a single entity where duplicate
objects are allowed
and insertion order
is preserved then we shoud go for List.
1. Insertion order
is preserved
2. Duplicate objects
are allowed
The main
implementation classes of List interface are:
1. ArrayList 2.
LinkedList 3. Vector 4. Stack
Demo Program to
describe List Properties:
import
java.util.ArrayList;
class Test
{
public static void
main(String[] args)
{
ArrayList<String>
l = new ArrayList<String>();
l.add("Sunny");
l.add("Bunny");
l.add("Chinny");
l.add("Sunny");
System.out.println(l);
}}
Output: [Sunny,
Bunny, Chinny, Sunny]
Note: List(may be
ArrayList,LinkedList,Vector or Stack) never talks about sorting
order. If we want
sorting for the list
then we should use Collections class sort() method.
Collecttions.sort(list)==>meant
for Default Natural Sorting Order
Collections.sort(list,Comparator)==>meant
for Customized Sorting Order
2. Set(I):
If we want to
represent a group of individual objects as a single entity where
duplicate objects are
not allowed and
insertion order is not preserved then we should go for Set.
1. Insertion order
is not preserved
2. Duplicates are
not allowed.If we are trying to add duplicates then we won't get any
error, just
add() method returns
false.
The following are
important Set implementation classes
1. HashSet 2.TreeSet
etc
Demo Program for
Set:
import
java.util.HashSet;
class Test
{
public static void
main(String[] args)
{
HashSet<String>
l = new HashSet<String>();
l.add("Sunny");
l.add("Bunny");
l.add("Chinny");
l.add("Sunny");
System.out.println(l);
}
}
Output: [Chinny,
Bunny, Sunny]
Note: In the case of
Set, if we want sorting order then we should go for: TreeSet
3. Map(I):
If we want to
represent objects as key-value pairs then we should go for
Map
Eg:
Rollno-->Name
mobilenumber-->address
The important
implementation classes of Map are:
1. HashMap 2.
TreeMap etc
Demo Program for
Map:
import
java.util.HashMap;
class Test
{
public static void
main(String[] args)
{
HashMap<String,String>
m= new HashMap<String,String>();
m.put("A","Apple");
m.put("Z","Zebra");
m.put("Durga","Java");
m.put("B","Boy");
m.put("T","Tiger");
System.out.println(m);
}
}
Output: {A=Apple,
B=Boy, T=Tiger, Z=Zebra, Durga=Java}
Sorted Collections:
1. Sorted List
2. Sorted Set
3. Sorted Map
1. Sorted List:
List(may be
ArrayList,LinkedList,Vector or Stack) never talks about sorting
order. If we want
sorting for the list
then we should use Collections class sort() method.
Collecttions.sort(list)==>meant
for Default Natural Sorting Order
For String objects:
Alphabetical Order
For Numbers :
Ascending order
Instead of Default
natural sorting order if we want customized sorting order then we
should go for
Comparator
interface.
Comparator interface
contains only one abstract method: compare()
Hence it is
Functional interface.
public int
compare(obj1,obj2)
returns -ve iff obj1
has to come before obj2
returns +ve iff obj1
has to come after obj2
returns 0 iff obj1
and obj2 are equal
Collections.sort(list,Comparator)==>meant
for Customized Sorting Order
Demo Program to Sort
elements of ArrayList according to Defaut Natural Sorting
Order(Ascending
Order):
import
java.util.ArrayList;
import
java.util.Collections;
class Test
{
public static void
main(String[] args)
{
ArrayList<Integer>
l = new ArrayList<Integer>();
l.add(10);
l.add(0);
l.add(15);
l.add(5);
l.add(20);
System.out.println("Before
Sorting:"+l);
Collections.sort(l);
System.out.println("After
Sorting:"+l);
}
}
Output:
Before Sorting:[10,
0, 15, 5, 20]
After Sorting:[0, 5,
10, 15, 20]
Demo Program to Sort
elements of ArrayList according to Customized Sorting
Order(Descending
Order):
import
java.util.TreeSet;
import
java.util.Comparator;
class MyComparator
implements Comparator<Integer>
{
public int
compare(Integer I1,Integer I2)
{
if(I1<I2)
{
return +1;
}
else if(I1>I2)
{
return -1;
}
else
{
return 0;
}
}
}
class Test
{
public static void
main(String[] args)
{
TreeSet<Integer>
l = new TreeSet<Integer>(new MyComparator());
l.add(10);
l.add(0);
l.add(15);
l.add(5);
l.add(20);
System.out.println(l);
}
}
//Descending order
Comparator
Output: [20, 15, 10,
5, 0]
Shortcut way:
import
java.util.ArrayList;
import
java.util.Comparator;
import
java.util.Collections;
class MyComparator
implements Comparator<Integer>
{
public int
compare(Integer I1,Integer I2)
{
return
(I1>I2)?-1:(I1<I2)?1:0;
}
}
class Test
{
public static void
main(String[] args)
{
ArrayList<Integer>
l = new ArrayList<Integer>();
l.add(10);
l.add(0);
l.add(15);
l.add(5);
l.add(20);
System.out.println("Before
Sorting:"+l);
Collections.sort(l,new
MyComparator());
System.out.println("After
Sorting:"+l);
}
}
Sorting with Lambda
Expressions:
As Comparator is
Functional interface, we can replace its implementation with Lambda
Expression
Collections.sort(l,(I1,I2)->(I1<I2)?1:(I1>I2)?-1:0);
Demo Program to Sort
elements of ArrayList according to Customized Sorting Order By
using Lambda
Expressions(Descending Order):
import
java.util.ArrayList;
import
java.util.Collections;
class Test
{
public static void
main(String[] args)
{
ArrayList<Integer>
l= new ArrayList<Integer>();
l.add(10);
l.add(0);
l.add(15);
l.add(5);
l.add(20);
System.out.println("Before
Sorting:"+l);
Collections.sort(l,(I1,I2)->(I1<I2)?1:(I1>I2)?-1:0);
System.out.println("After
Sorting:"+l);
}
}
Output:
Before Sorting:[10,
0, 15, 5, 20]
After Sorting:[20,
15, 10, 5, 0]
2. Sorted Set
In the case of Set,
if we want Sorting order then we should go for TreeSet
1. TreeSet t = new
TreeSet();
This TreeSet object
meant for default natural sorting order
2. TreeSet t = new
TreeSet(Comparator c);
This TreeSet object
meant for Customized Sorting Order
Demo Program for
Default Natural Sorting Order(Ascending Order):
import
java.util.TreeSet;
class Test
{
public static void
main(String[] args)
{
TreeSet<Integer>
t = new TreeSet<Integer>();
t.add(10);
t.add(0);
t.add(15);
t.add(5);
t.add(20);
System.out.println(t);
}
}
Output: [0, 5, 10,
15, 20]
Demo Program for
Customized Sorting Order(Descending Order):
import
java.util.TreeSet;
class Test
{
public static void
main(String[] args)
{
TreeSet<Integer>
t = new TreeSet<Integer>((I1,I2)->(I1>I2)?-1:(I1<I2)?1:0);
t.add(10);
t.add(0);
t.add(15);
t.add(25);
t.add(5);
t.add(20);
System.out.println(t);
}
}
Output: [25, 20, 15,
10, 5, 0]
Sorted Map:
In the case of Map,
if we want default natural sorting order of keys then we should go
for
TreeMap.
TreeMap m = new
TreeMap();
This TreeMap object
meant for default natural sorting order of keys
TreeMap t = new
TreeMap(Comparator c);
This TreeMap object
meant for Customized Sorting Order of keys
Demo Program for
Default Natural Sorting Order(Ascending Order):
import
java.util.TreeMap;
class Test
{
public static void
main(String[] args)
{
TreeMap<Integer,String>
m = new TreeMap<Integer,String>();
m.put(100,"Durga");
m.put(600,"Sunny");
m.put(300,"Bunny");
m.put(200,"Chinny");
m.put(700,"Vinny");
m.put(400,"Pinny");
System.out.println(m);
}
}
Output: {100=Durga,
200=Chinny, 300=Bunny, 400=Pinny, 600=Sunny, 700=Vinny}
Demo Program for
Customized Sorting Order(Descending Order):
import
java.util.TreeMap;
class Test
{
public static void
main(String[] args)
{
TreeMap<Integer,String> m = new
TreeMap<Integer,String>((I1,I2)->(I1<I2)?1:(I1>I2)?-
1:0);
m.put(100,"Durga");
m.put(600,"Sunny");
m.put(300,"Bunny");
m.put(200,"Chinny");
m.put(700,"Vinny");
m.put(400,"Pinny");
System.out.println(m);
}
}
Output: {700=Vinny,
600=Sunny, 400=Pinny, 300=Bunny, 200=Chinny, 100=Durga}
Sorting for
Customized class objects by using Lambda Expressions:
import
java.util.ArrayList;
import
java.util.Collections;
class Employee
{
int eno;
String ename;
Employee(int
eno,String ename)
{
this.eno=eno;
this.ename=ename;
}
public String
toString()
{
return
eno+":"+ename;
}
}
class Test
{
public static void
main(String[] args)
{
ArrayList<Employee>
l= new ArrayList<Employee>();
l.add(new
Employee(100,"Katrina"));
l.add(new
Employee(600,"Kareena"));
l.add(new
Employee(200,"Deepika"));
l.add(new
Employee(400,"Sunny"));
l.add(new
Employee(500,"Alia"));
l.add(new
Employee(300,"Mallika"));
System.out.println("Before
Sorting:");
System.out.println(l);
Collections.sort(l,(e1,e2)->(e1.eno<e2.eno)?-1:(e1.eno>e2.eno)?1:0);
System.out.println("After
Sorting:");
System.out.println(l);
}
}
Output:
Before Sorting:
[100:Katrina,
600:Kareena, 200:Deepika, 400:Sunny, 500:Alia, 300:Mallika]
After Sorting:
[100:Katrina,
200:Deepika, 300:Mallika, 400:Sunny, 500:Alia, 600:Kareena]
Comments
Post a Comment