0% found this document useful (0 votes)
2 views

Java Fundamentals. Lesson 18_ Simple Lambda Expressions

This lesson covers the syntax and usage of lambda expressions in Java, explaining how they can simplify code by replacing traditional method implementations with concise expressions. It introduces the Predicate interface and demonstrates how to use lambda expressions for validation tasks, highlighting their advantages over conventional class implementations. The lesson also discusses method references and provides exercises to reinforce understanding of these concepts.

Uploaded by

Denis Moscvin
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

Java Fundamentals. Lesson 18_ Simple Lambda Expressions

This lesson covers the syntax and usage of lambda expressions in Java, explaining how they can simplify code by replacing traditional method implementations with concise expressions. It introduces the Predicate interface and demonstrates how to use lambda expressions for validation tasks, highlighting their advantages over conventional class implementations. The lesson also discusses method references and provides exercises to reinforce understanding of these concepts.

Uploaded by

Denis Moscvin
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 50

Java Fundamentals

Lesson 18: Simple Lambda Expressions

Speaker: Nicolae Sîrbu


Alexandru Umaneț
Lesson Objectives

● Understand the syntax and usage of lambda expressions

● Get insights on how to use the Predicate interface

● Write simple lambda expressions that consume a lambda Predicate


What is a lambda expression?

Lambda expressions are anonymous methods (methods without names) used to implement a method
defined by a functional interface.

A functional interface is an interface that contains one and only one abstract method.

@FunctionalInterface
interface NumberValidator {
boolean validate(int n);
}
Coding without lambda expressions

Given the following interface and class: class Emp {

@FunctionalInterface String name;


interface EmpValidator { int performanceRating;
boolean validate(Emp emp);
} Emp(String name, int rating) {
this.name = name;
this.performanceRating = rating;
}

public String getName() {


return name;
}

public int getPerformanceRating() {


return performanceRating;
}

public String toString() {


return name + ":" + performanceRating;
}
}
Coding without lambda expressions

To use the interface EmpValidator you should define a class that implements it.

class ValidatePerformanceRating implements EmpValidator {

@Override
public boolean validate(Emp emp) {
return (emp.getPerformanceRating() >= 5);
}
}
Coding without lambda expressions

What happens if you want to check another attribute of an Emp instance, say, name?

class ValidateName implements EmpValidator {

@Override
public boolean validate(Emp emp) {
return (emp.getName.startsWith("P"));
}
}

Note how just the boolean condition is changing in the check methods in the classes ValidateName
and ValidatePerformanceRating.
Coding without lambda expressions

class Test {
public static void main(String args[]) {
Emp e1 = new Emp("Shreya", 5);
Emp e2 = new Emp("Paul", 4);

ArrayList<Emp> empArrList = new ArrayList<>();


empArrList.add(e1);
empArrList.add(e2);

EmpValidator validatePerformance = new ValidatePerformanceRating();


filter(empArrList, validatePerformance);
}

static void filter(ArrayList<Emp> list, EmpValidator rule) {


for (Emp emp : list) {
if (rule.validate(emp)) {
System.out.println(emp);
}
}
}
}
Lambdas to the rescue!

You’ll need to create multiple classes (that implement the interface EmpValidator) to use different
validity rules. Apart from being mostly repetitive, it’s verbose too.

Let’s remove the definition of the classes ValidateName and ValidatePerformanceRating.

To define the validation condition, we’ll use lambdas (continued on next slide):
Coding with lambda expressions

class Test {
public static void main(String args[]) {
Emp e1 = new Emp("Shreya", 5);
Emp e2 = new Emp("Paul", 4);

ArrayList<Emp> empArrList = new ArrayList<>();


empArrList.add(e1);
empArrList.add(e2);

EmpValidator validatePerformance = emp -> emp.getPerformanceRating() >= 5;


filter(empArrList, validatePerformance);
}

static void filter(ArrayList<Emp> list, EmpValidator rule) {


for (Emp emp : list) {
if (rule.validate(emp)) {
System.out.println(emp);
}
}
}
}
Coding with lambda expressions

In the preceding code, there isn’t any change to the method filter that accepts a method parameter of
type EmpValidator (an interface).

The code defines a lambda expression. It defines code to be passed to the method filter.

EmpValidator validatePerformance = new ValidatePerformanceRating();


filter(empArrList, validatePerformance);

vs

EmpValidator validatePerformance = emp -> emp.getPerformanceRating() >=


5;
filter(empArrList, validatePerformance);
Method vs Lambda

The correlation between a lambda expression and a regular method signature:

class ValidatePerformanceRating implements EmpValidator {


public boolean validate(Emp emp) {
return emp.getPerformanceRating() >= 5;
}
}

EmpValidator validatePerformance = emp -> emp.getPerformanceRating() >= 5;


Method vs Lambda

- The method validate accepts only one method parameter and so does the lambda expression,
that is, (emp).
- The method validate returns a boolean value and so does the expression
emp.getPerformanceRating() >= 5 in the lambda expression.
Syntax of lambda expressions

Lambda expressions introduce the new arrow operator '->' into Java. It divides the lambda expressions
in two parts:

(args1, args2, args3, ……) -> { body }

The left side specifies the parameters required by the expression, which could also be empty if no
parameters are required.

The right side is the lambda body which specifies the actions of the lambda expression.
Syntax of lambda expressions

Let’s revisit the lambda expression used in the previous expression:

EmpValidator validatePerformance = e -> e.getPerformanceRating() >= 5;


Syntax of lambda expressions

Each lambda expression has multiple optional and mandatory sections:

● Parameter type (optional)


● Parameter name (mandatory)
● Arrow (mandatory)
● Curly braces (optional)
● Keyword return (optional)
● Lambda body (mandatory)

(Emp e) -> { return e.getPerformanceRating() >= 5; };


Variations of a lambda expression

The following are valid variations of the preceding lambda expression:

EmpValidator validate = (e) -> e.getPerformanceRating() >= 5;


EmpValidator validate = e -> e.getPerformanceRating() >= 5;
EmpValidator validate = (Emp e) -> e.getPerformanceRating() >= 5;
EmpValidator validate = (e) -> { return (e.getPerformanceRating() >= 5); };
EmpValidator validate = e -> { return (e.getPerformanceRating() >= 5); };

The return value of the lambda expression must match or must be compatible with the return value of
the abstract method defined in the interface.
Incorrect use of a lambda expression

EmpValidator validatePerformance = (Emp e) -> 5;

EmpValidator validatePerformance = (e, f) -> { return true; };


Some facts about lambda expressions

A lambda expression can receive zero, one or more parameters:

● () -> { do something }
● (int a) -> { do something }
● (int a, int b ,……,n) -> { do something }
Some facts about lambda expressions

Parameters, if there are more than one, must be enclosed in parenthesis and also must be separated with
commas:

● (a, b) -> { do something }


● (int a, int b) -> { do something }
Some facts about lambda expressions

It is not mandatory to use parentheses when there is a single parameter:

● a -> { do something }
Some facts about lambda expressions

When there is a single statement in its body, curly brackets are not mandatory.

a -> a > 45;

However, if the lambda expression needs to contain several statements, then they must be wrapped
within curly brackets and an explicit return statement must be declared.

a -> {
System.out.println(“Inside lambda expressions”);
return a > 45;
}
Some facts about lambda expressions

Lambdas are allowed to access variables like any other ordinary method does.

boolean wantWhetherCanHop = true;


print(animals, a -> a.canHop() == wantWhetherCanHop);
More lambda expressions

Let’s pretend that there are valid interfaces that can consume a lambda with zero, one, or two String
parameters. Here’s how lambda expressions might look like:
Java 8 Method Reference
Java 8 Method Reference

A method reference is the shorthand syntax for a lambda expression that executes just ONE method.

Here's the general syntax of a method reference:


object::methodName
or
Class::methodName

In a method reference, you place the object or class that contains the method before the :: (double
colon) operator and the name of the method after it without arguments.
Java 8 Method Reference

There are four types of method references:


I. a method reference to a static method;
II. a method reference to an instance method of an object of a particular type;
III. a method reference to an instance method of an existing object;
IV. a method reference to a constructor.
I. Static method reference

In this case, we have a lambda expression like the one below:


(args) -> Class.staticMethod(args)

This can be turned into the following method reference:


Class::staticMethod

Notice that between a static method and a static method reference, instead of the . (dot) operator,
we use the :: (double colon) operator, and that we don't pass arguments to the method reference.
I. Static method reference

We’ll begin with a very simple example, capitalizing and printing a list of Strings:

List<String> messages = Arrays.asList("hello", "baeldung", "readers!");

We can achieve this by leveraging a simple lambda expression calling the StringUtils.capitalize()
method directly:

messages.forEach(word -> StringUtils.capitalize(word));

Or, we can use a method reference to simply refer to the capitalize static method:

messages.forEach(StringUtils::capitalize);
II. Instance method reference of an object of a particular type

In this case, we have a lambda expression like the following:

(obj, args) -> obj.instanceMethod(args)

where an instance of an object is passed, and one of its methods is executed with some optional(s)
parameter(s).

This can be turned into the following method reference:


ObjectType::instanceMethod
II. Instance method reference of an object of a particular type

List<Person> people = Arrays.asList(new Person("dan"), new Person("laura"));

If we use a classic lambda expression, the parameter needs to be explicitly passed, while using a method
reference is much more straightforward:

// Method reference
people.forEach(Person::printName);

// Lambda expression
people.forEach(person -> person.printName());
III. Instance method reference of an existing object

In this case, we have a lambda expression like the following:


(args) -> obj.instanceMethod(args)

This can be turned into the following method reference:


obj::instanceMethod
III. Instance method reference of an existing object

final List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

final MyComparator myComparator = new MyComparator();

// Method reference
Collections.sort(list, myComparator::compare);

// Lambda expression
Collections.sort(list, (a,b) -> myComparator.compare(a,b));
IV. Constructor method reference

In this case, we have a lambda expression like the following:


(args) -> new ClassName(args)

That can be turned into the following method reference:


ClassName::new

The only thing this lambda expression does is to create a new object and we just reference a constructor
of the class with the keyword new.
IV. Constructor method reference

Let’s create a Bicycle array out of a String list with different brands:
List<String> bikeBrands = Arrays.asList("Giant", "Scott", "Trek", "GT");

First, we’ll add a new constructor to our Bicycle class:


public Bicycle(String brand) {
this.brand = brand;
this.frameSize = 0;
}

Next, we’ll use our new constructor from a method reference and make a Bicycle array from the original
String list:
bikeBrands.stream().map(Bicycle::new).toArray(Bicycle[]::new);
Interface Predicate
Interface Predicate

Predicate is a functional interface. Here’s the partial definition of this interface:

@FunctionalInterface
public interface Predicate<T> {

/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the
predicate,
* otherwise {@code false}
*/
boolean test(T t);
Interface Predicate

In the preceding code, the class declaration includes <T>, which declares that Predicate is a generic
interface, which isn’t limited to a particular type. It can be used with multiple types.

To use Predicate in your code, your method must accept a parameter of type Predicate and you
must use its public method test to evaluate an argument.
Interface Predicate

class Test {
public static void main(String args[]) {
Emp e1 = new Emp("Shreya", 5);
Emp e2 = new Emp("Paul", 4);

ArrayList<Emp> empArrList = new ArrayList<>();


empArrList.add(e1);
empArrList.add(e2);

Predicate<Emp> predicate = emp -> emp.getPerformanceRating() >= 5;


filter(empArrList, predicate);
}

static void filter(ArrayList<Emp> list, Predicate<Emp> rule) {


for (Emp emp : list) {
if (rule.test(emp)) {
System.out.println(emp);
}
}
}
}
Interface Predicate

Java 8 has also modified many of its existing API methods, which work with functional interfaces like
Predicate. For example, the class ArrayList defines the method removeIf, which accepts a method
parameter of type Predicate.

Emp e1 = new Emp("Shreya", 5);


Emp e2 = new Emp("Paul", 4);

ArrayList<Emp> empArrList = new ArrayList<>();


empArrList.add(e1);
empArrList.add(e2);

empArrList.removeIf(emp -> emp.getName().startsWith("S"));


Using Predicate. Use case #1

// Creating predicate
Predicate<Integer> lessThan = i -> (i < 18);

// Calling Predicate method


System.out.println(lessThan.test(10));
Using Predicate. Use case #2

public static void main(String[] args) {


Predicate<Integer> greaterThanZero = a -> a > 0;
Predicate<Integer> notZero = a -> a != 0;

validateNumber(10, greaterThanZero);
validateNumber(10, a -> a != 0);

public static boolean validateNumber(int number, Predicate<Integer> rule) {


return rule.test(number);
}
Using Predicate. Use case #3. Predicate chain

// Creating predicate
Predicate<Integer> greaterThanTen = num -> num > 10;
Predicate<Integer> lowerThanTwenty = num -> num < 20;

boolean result = greaterThanTen.and(lowerThanTwenty).test(15);


System.out.println(result); // True
Using Predicate. Use case #4. Predicate chain

// Creating predicate
Predicate<String> hasLengthOf10 = str -> str.length() > 10;
Predicate<String> containsLetterA = str -> str.contains("A");

boolean result = hasLengthOf10.or(containsLetterA).test("And");


System.out.println(result); // True
Q&A #1


Q&A #2


Exercise #18.1

Define an interface that would help validating a number against different conditions:

● Greater than zero;


● Less than zero;
● Equal to zero;
● Even;
● Odd.

a. Create appropriate validator classes and make sure they work as expected.
b. Implement an alternative for the validators built in #a, using Java lambda expressions. Test them.
c. Implement the same validation rules, using the Predicate interface.
Exercise #18.2

Define an interface that would help validating a String against different conditions:

● Contains the string “knowledge”;


● Has a length greater than 10;
● Is not null;
● Is not a String full of empty spaces.

a. Create appropriate validator classes and make sure they work as expected.
b. Implement an alternative for the validators built in #a, using Java lambda expressions. Test them.
c. Implement the same validation rules, using the Predicate interface.
Resources

Lambda Expressions and Functional Interfaces: Tips and Best Practices


(https://www.baeldung.com/java-8-lambda-expressions-tips)

Lambda expressions
(https://howtodoinjava.com/java8/lambda-expressions/)

Method References in Java


(https://www.baeldung.com/java-method-references)

Java Predicate
(https://www.concretepage.com/java/java-8/java-predicate)
Java Fundamentals
Lesson 18: Simple Lambda Expressions
End.

Speaker: Nicolae Sîrbu


Alexandru Umaneț

You might also like