Lambda Expressions

It is a new feature which is introduced in Java 8. Lambdas will bring a big change in Java majorly in term of how Java code will be written and how it will be designed. Some of changes are inspired by functional programming languages. In functional programming, you can define functions, give them reference variables and pass them as method arguments and much more like java script.

A lambda expression is just a shorter way of writing the implementation of a method for later execution. lambda expressions is also referred as anonymous method without name and identifiers.

Syntax: lambda essentially consists of three parts which can either be a single expression or a block of Java code

  • parenthesized set of parameters

  • arrow

  • body

(argument-parameter)->{body}

Example :

(int x, int y)->x+y // take 2 int value and return sum of values

(x, y)->x+y // take 2 number and return sum of values

(String s) -> System.out.print(s)//take string param and print value

Functional interfaces

In Java 8, those interface requires exactly one method to be implemented means interfaces with only one single method referred as functional interfaces.

Example : Runnable , Callable<T> , Comparator<T> interface etc.

Java 8 designers have chosen to give us an annotation @FunctionalInterface for functional interfaces. But compiler determines by “functional interfaces ” from the structure of the interface, not @FunctionalInterface annotation.

Before Lambda: we write code for Runnable like below

public class JavaOops{

public static void main(String args[]) {

Runnable runnable = new Runnable() {

public void run() {

System.out.println("www.javaoops.com");

}

};

runnable.run();

}

}

After Lambda: we can write code using lambda like below.

public class JavaOops{

public static void main(String args[]) {

Runnable runnable= () -> System.out.println("www.javaoops.com");

runnable.run();

}

}

Both code result will be same. Output : www.javaoops.com.

Above example, we have using no argument in method and no return type. we can use with method arguments and return type or break and continue keyword as well in lambda.

public class JavaOops{

public static void main(String args[]) {

Comparator<String> c =

(String str1, String str2) ->

{

System.out.println("Compare" +str1 + " with " + str2);

return str1.compareTo(str2);

};

int output = c.compare("Java", "OOPS");

System.out.println(output);

}

}

Type inference

As per functional programming, Type inference introduce by Java. Compiler can identify type of parameters pass in method without re-type by developer.

As per below example, We are not going to re-declare type of str1 and str2 but compile can identify it would be string type based on pass augments in double quote (" " ) during call compare method.

public class JavaOops{

public static void main(String args[]) {

Comparator<String> c =

(str1,str2) ->

{

System.out.println("Compare" +str1 + " with " + str2);

return str1.compareTo(str2);

};

int output = c.compare("Java", "OOPS");

System.out.println(output);

}

}

Lexical scoping

One thing that is new in lambda expression. Lambdas are lexical scoped means Lambda expression doesn’t have a scope of its own, it has the same scope as its enclosing scope. Since , lambda expression does not introduce a new level of scoping you can directly access fields, methods, and local variables of the enclosing scope.

If you are using this keyword and super keyword with in a lambda. It will refers to same object that has invoked the method where the lambda expression resides.

Without Lambda

class JavaOops{

public Runnable runnable = new Runnable() {

public void run() {

System.out.println(this);

System.out.println(toString());

}

};

public String toString() {

return "www.javaoops.com";

}

}

public class TestingInnerClass {

public static void main(String args[]) {

JavaOops javaOops = new JavaOops();

javaOops.runnable.run();

}

}

Output :

JavaOops$1@15db9742

JavaOops$1@15db9742

In this example, We are using this keyword and toString() method. When we are trying to print this and toString(). output will showing machine code beacuse inner class have own scope. which can not reach out outer class scope. But when we will use lambda output will be different because lambda doesn't have own scope as inner class. lambda will refer same scope where lambda reside.

With Lambda

class JavaOops{

public Runnable runnable = () -> {

System.out.println(this);

System.out.println(toString());

};

public String toString() {

return "www.javaoops.com";

}

}

public class TestingInnerClass{

public static void main(String args[]) {

JavaOops javaOops = new JavaOops();

javaOops.runnable.run();

}

}

Output :

www.javaoops.com

www.javaoops.com

Lambda expression has lexical scope if same name define in lambda. Compiler will give error because same name as local variable str1 already defined in scope.

String str1 = "javaoops.com";

// Error

Comparator<String> com = (str1, str2) -> s1.length() - s2.length();

Effectively final

Before Java 8, it was mandatory to define such a field (which is used in inner class) as final, In Lambda , it is relaxed a bit and there is no need to explicitly declare such fields as final.

Method references

This is great for one-off kinds of behavior, Method reference is used to refer method of functional interface

Syntax

Object :: methodName

In a method reference, you place the object (or class) that contains the method that you want to call before the delimiter " :: " operator and the name of the method is provided after it without arguments.

Types of Method References

  • Reference to a static method : (ClassName::staticMethodName)

  • Reference to an instance method: (object::instanceMethodName)

  • Reference to a constructor : (ClassName::new)

Reference to a static method

interface JavaOopsFunctionalInterface{

void print();

}


public class JavaOopsMethodReference {

public static void printJavaOops(){

System.out.println("www.javaoops.com: Reference to static");

}

public static void main(String[] args) {

//Referring static method

JavaOopsFunctionalInterface javaOopsFunctionalInterface = JavaOopsMethodReference::printJavaOops;

//Calling functional interface method

javaOopsFunctionalInterface.print();

}

}



Reference to an instance method

interface JavaOopsFunctionalInterface{

void print();

}


public class JavaOopsMethodReference {

public void printJavaOops(){

System.out.println("www.javaoops.com:Reference to instance");

}

public static void main(String[] args) {

JavaOopsMethodReference obj = new JavaOopsMethodReference();

// Referring class obj

JavaOopsFunctionalInterface javaOopsFunctionalInterface = obj::printJavaOops;

// Calling interface method

javaOopsFunctionalInterface.print();

}

}

Reference to a constructor

interface JavaOopsFunctionalInterface{

void print();

}


public class JavaOopsMethodReference {

public JavaOopsMethodReference(){

System.out.println("www.javaoops.com:Reference to constructor");

}


public static void main(String[] args) {

// Referring static method

JavaOopsFunctionalInterface javaOopsFunctionalInterface = JavaOopsMethodReference::new;

// Calling interface method

javaOopsFunctionalInterface.print();

}

}


Lambdas will bring a lot of change to Java, both in terms of how Java code will be written and how it will be designed.

Java 11

Before Java 11, it was not possible to use Var inside a lambda expression and it used to give compilation issue. However var keyword was allowed to use for type interference everywhere by Java 10 itself. Sometime it was a cause of confusion for developers.

With Java 11, Oracle has improvised the language to support var keyword in lambda expression as well. It actually doesn’t bring any value other than giving a uniformity in language operations and hence better readability for developers. Some may say that it makes it a little verbose hence it is not mandatory to use it always. It is an option java has given for developers.


public class JavaOopsVarInLamdaExpression {

public static void main(String[] args) {

//type inference using var (feature of Java 10)

var arrayOfStrings = new String[] {"java", "oops", "lambda", "var"};

var javaoops = Arrays.stream(arrayOfStrings).anyMatch(

(var str) -> (str != null && str.contains("lambda")));

//var can be used with local variable for uniformity

System.out.println(javaoops );

}

}