Tuesday, July 25, 2023

Java Lambda Expressions: Transforming Your Approach to Functional Interfaces

java,lambda expression,functional programming,consumer,predicate,programming,software development,technology Java is a versatile and powerful programming language that has grown in popularity over the years. Java 8 introduced several new features to make code more concise and expressive. Lambda expressions, as well as functional interfaces such as Consumer and Predicate, are just a few of the powerful features that have transformed Java programming. In this blog post, we'll look at what lambda expressions are, how they work, and how they improve the elegance and functionality of Java code.

Lambda Expressions

Lambda expressions, which were introduced in Java 8, are a concise way of expressing anonymous functions. Lambda expressions allow you to treat behavior as data, making it easier to pass it as arguments to methods or store it in variables. They were added in Java 8 to help with functional programming in the language.

Lambda expressions consist of three main components:
  1. Parameters: These are the lambda expression's input arguments. They can have a value of zero or more and are specified within parentheses. When there is only one parameter, the brackets are not required. Even if there are no parameters, you must use empty brackets.
  2. Arrow Operator (->): This detaches the parameters from the lambda expression's body. It's called the "arrow operator" because it resembles an arrow pointing from the parameters to the body.
  3. Body: This is the code that will be run when the lambda expression is called. It can be a single statement or a block of code surrounded by curly braces. If it's a single expression, the lambda's return value is the expression's value. If it's a block of code, you can specify the return value explicitly with a return statement.
Lambda Expression, Consumer And Predicate Functional Interfaces in Java 8

First, we'll write a simple Java program that doesn't use a Lambda expression. It's right here:
public class HelloWorld implements IIncrement, IConcatenate {

    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        System.out.println("Number increment :: " + helloWorld.increment(10));
        System.out.println("String concatenation :: " + helloWorld.concatenate("Hello", "World!!!"));
    }

    @Override
    public int increment(int a) {
        return a + 5;
    }

    @Override
    public String concatenate(String a, String b) {
        return a + " " + b;
    }
}

interface IIncrement {
    int increment(int a);
}

interface IConcatenate {
    String concatenate(String a, String b);
}
Output

  Number increment :: 15
  String concatenation :: Hello World!!!
  
The preceding code is an example of a traditional Java program in which the HelloWorlds class implements two interfaces: IIncrement and IConcatenate. It exemplifies imperative programming.

We will now rewrite the preceding code using the Lambda expression:
public class HelloWorld {

    public static void main(String[] args) {
        IIncrement increment = (a) -> {
            return a + 5;
        };
        System.out.println("Number increment :: " + increment.increment(10));

        IConcatenate concatenate = (a, b) -> {
            return a + " " + b;
        };
        System.out.println("String concatenation :: " + concatenate.concatenate("Hello", "World!!!"));
    }
}

interface IIncrement {
    int increment(int a);
}

interface IConcatenate {
    String concatenate(String a, String b);
}
Output

  Number increment :: 15
  String concatenation :: Hello World!!!
  
We have two interfaces here: IIncrement and IConcatenate, which have abstract methods increment(int a) and concatenate(String a, String b). We used the Lambda expression to create instances of these interfaces within the main method. In contrast to the previous program, the HelloWorld class does not implement the interfaces. We have only provided the implementation of the interfaces' methods here.

Here's another example of how to use the lambda expression to create a Thread.
public class ThreadLambdaExpressionExample {
    public static void main(String args[]) {
      Runnable runnable1 = () -> {
            int sum = 0;
            for (int i = 0; i < 10; i++) {
                if(i%2 == 0){
                    sum += i;
                }
            }
            System.out.println("Sum of even numbers :: " + sum);
        };
        (new Thread(runnable1)).start();
        
        (new Thread(() -> {
            int sum = 0;
            for (int i = 0; i < 10; i++) {
                if(i%2 != 0){
                    sum += i;
                }
            }
            System.out.println("Sum of odd numbers :: " + sum);
        })).start();
    }
}
Output

  Sum of even numbers :: 20
  Sum of odd numbers :: 25
  

Functional Interface

The concept of a functional interface is closely related to the concept of functional programming, which emphasizes writing programs with pure functions and treating functions as first-class citizens. A functional interface is one that has only one abstract method, which is known as the functional method.

To define a functional interface, you must use the @FunctionalInterface annotation, which is optional. This annotation is not required, but using it ensures that the interface has only one abstract method. If the interface contains more than one abstract method, the compiler will generate an error.

We have interfaces with only one abstract method in the previous Lambda expression example. Lambda expressions are frequently used with functional interfaces. The context in which the lambda expression is used determines its type, which is why it is critical to use functional interfaces that match the lambda's signature.

In Java 8, the java.util.function package added several built-in functional interfaces to cover various common use cases, such as Consumer, Supplier, Predicate, Function, and so on. These interfaces have already been annotated with @FunctionalInterface and can be used with lambda expressions directly.

Function Interface

It denotes a function that accepts one argument and returns a result. It takes two arguments: input and output. This interface has several functional methods, including apply(object), andThen(object), and compose(object). Now consider the Function interface example.
import java.util.function.Function;

public class FunctionInterfaceExample {
    public static void main(String[] args) {
        Function<Integer, Boolean> oddEvenFunction = (n) -> {
            return (n%2 == 0 ? true : false);
        };
        System.out.println("Is 64 even number :: " + oddEvenFunction.apply(8));

        Function<String, String> upperCaseFunction = (n) -> n.toUpperCase();
        Function<String, String> concatFunction = (n) -> n.concat(" Interface");
                
        System.out.println(upperCaseFunction.apply("functional programing"));

        System.out.println(upperCaseFunction.andThen(concatFunction).apply("function"));

        System.out.println(upperCaseFunction.compose(concatFunction).apply("function"));
    }
}
Output

  Is 64 even number :: true
  FUNCTIONAL PROGRAMING
  FUNCTION Interface
  FUNCTION INTERFACE
  

The apply() method is used to invoke the function, which means that the lambda expression contained within the function will be executed. The apply() method is also used to pass a value to a specific function.

The andThen() and compose() methods are used to connect multiple functions to achieve a specific result.

When we use the andThen() method to connect two functions, the parameterized function that we passed into andThen() is executed after the first function. So, in this case, upperCaseFunction will run first, followed by concatFunction.

In the case of the compose() method, the parameterized function that we passed in will be executed first, followed by the first function. So concatFunction will be executed first, followed by upperCaseFunction.

Movie List

We've written a static method that returns a list of Movies. This movie list is used later in the tutorial.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MovieList {
    public static List<Movie> getMovieList() {
        List<Movie> movieList = new ArrayList<>();

        Movie movie = new Movie("Guardians of the Galaxy", "Sci-Fi", "2014",
                121, 8.1, 757074, 333.13,
                Arrays.asList("Chris Pratt", "Vin Diesel", "Bradley Cooper", "Zoe Saldana"));
        movieList.add(movie);

        movie = new Movie("Prometheus", "Sci-Fi", "2012",
                124, 7, 485820, 126.46,
                Arrays.asList("Noomi Rapace", "Logan Marshall-Green", "Michael Fassbender", "Charlize Theron"));
        movieList.add(movie);

        movie = new Movie("Split", "Horror", "2016",
                117, 7.3, 157606, 138.12,
                Arrays.asList("James McAvoy", "Anya Taylor-Joy", "Haley Lu Richardson", "Jessica Sula"));
        movieList.add(movie);

        movie = new Movie("Passengers", "Adventure", "2016",
                116, 7, 192177, 100.01,
                Arrays.asList("Jennifer Lawrence", "Chris Pratt", "Michael Sheen", "Laurence Fishburne"));
        movieList.add(movie);

        movie = new Movie("Frozen", "Animation", "2013",
                102, 7.5, 451894, 400.74,
                Arrays.asList("Kristen Bell", "Idina Menzel", "Jonathan Groff", "Josh Gad"));
        movieList.add(movie);

        movie = new Movie("12 Years a Slave", "Drama", "2013",
                134, 8.1, 486338, 56.67,
                Arrays.asList("Chiwetel Ejiofor", "Michael Kenneth Williams", "Michael Fassbender", "Brad Pitt"));
        movieList.add(movie);

        movie = new Movie("Into the Woods", "Drama", "2014",
                125, 6, 109756, 128,
                Arrays.asList("Anna Kendrick", "Meryl Streep", "Chris Pine", "Emily Blunt"));
        movieList.add(movie);

        return movieList;
    }
}

class Movie {
    String name;
    String genre;
    String year;
    int lengthMinutes;
    double rating;
    int votes;
    double revenue;
    List<String> actors;

    public Movie(String name, String genre, String year, int lengthMinutes, double rating, int votes, double revenue,
            List<String> actors) {
        this.name = name;
        this.genre = genre;
        this.year = year;
        this.lengthMinutes = lengthMinutes;
        this.rating = rating;
        this.votes = votes;
        this.revenue = revenue;
        this.actors = actors;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGenre() {
        return genre;
    }

    public void setGenre(String genre) {
        this.genre = genre;
    }

    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }

    public int getLengthMinutes() {
        return lengthMinutes;
    }

    public void setLengthMinutes(int lengthMinutes) {
        this.lengthMinutes = lengthMinutes;
    }

    public double getRating() {
        return rating;
    }

    public void setRating(double rating) {
        this.rating = rating;
    }

    public int getVotes() {
        return votes;
    }

    public void setVotes(int votes) {
        this.votes = votes;
    }

    public double getRevenue() {
        return revenue;
    }

    public void setRevenue(double revenue) {
        this.revenue = revenue;
    }

    public List<String> getActors() {
        return actors;
    }

    public void setActors(List<String> actors) {
        this.actors = actors;
    }

    @Override
    public String toString() {
        return "Movie [name=" + name + ", genre=" + genre + ", year=" + year + ", lengthMinutes=" + lengthMinutes
                + ", rating=" + rating + ", votes=" + votes + ", revenue=" + revenue + ", actors=" + actors + "]";
    }

}

Consumer Interface

The java.util.function package contains the Consumer interface. Java 8 introduced the Consumer interface. It's used in Java to implement functional programming. It denotes a function that takes a single argument and returns a result. It has the following functional methods: accept() and andThen(). Consider the following example of a consumer interface:
import java.util.function.Consumer;

public class ConsumerExample {
    public static void main(String[] args) {
        Consumer<String> messageConsumer = (a) -> {
            System.out.println("Consumer returns :: " + a);
        };
        messageConsumer.accept("Hello from Consumer!!!");

        Consumer<Integer> mathConsumer = (a) -> {
            System.out.println("a*a = " + a * a);
        };
        mathConsumer.accept(4);
        System.out.println("----------------------");
        
        List<Movie> movieList = MovieList.getMovieList();

        // consumer to get all movie details
        Consumer<Movie> consumerMovie = (c) -> System.out.println(c);
        movieList.forEach(consumerMovie);
        System.out.println("----------------------------");

        // consumer to get all movie names
        Consumer<Movie> consumerName = (m) -> System.out.println(m.getName());
        movieList.forEach(consumerName);
        System.out.println("----------------------------");

        // consumer to get all movie name with it actor names
        Consumer<Movie> consumerActors = (m) -> System.out.println(m.getActors());
        movieList.forEach(consumerName.andThen(consumerActors));
        System.out.println("----------------------------");

        // movie details with genre 'Drama'
        movieList.forEach(cons -> {
            if (cons.getGenre().equals("Drama")) {
                consumerMovie.accept(cons);
            }
        });
        System.out.println("----------------------------");

        // movie name and actor with genre 'Drama' and rating is greater than 6
        movieList.forEach(cons -> {
            if (cons.getGenre().equals("Drama") && cons.getRating() > 6) {
                consumerName.andThen(consumerActors).accept(cons);
            }
        });
    }
}
Output

  Consumer returns :: Hello from Consumer!!!
  a*a = 16
  ----------------------
  Movies with it details :: 
  Movie [name=Guardians of the Galaxy, genre=Sci-Fi, year=2014, lengthMinutes=121, rating=8.1, votes=757074, revenue=333.13, actors=[Chris Pratt, Vin Diesel, Bradley Cooper, Zoe Saldana]]
  Movie [name=Prometheus, genre=Sci-Fi, year=2012, lengthMinutes=124, rating=7.0, votes=485820, revenue=126.46, actors=[Noomi Rapace, Logan Marshall-Green, Michael Fassbender, Charlize Theron]]
  Movie [name=Split, genre=Horror, year=2016, lengthMinutes=117, rating=7.3, votes=157606, revenue=138.12, actors=[James McAvoy, Anya Taylor-Joy, Haley Lu Richardson, Jessica Sula]]
  Movie [name=Passengers, genre=Adventure, year=2016, lengthMinutes=116, rating=7.0, votes=192177, revenue=100.01, actors=[Jennifer Lawrence, Chris Pratt, Michael Sheen, Laurence Fishburne]]
  Movie [name=Frozen, genre=Animation, year=2013, lengthMinutes=102, rating=7.5, votes=451894, revenue=400.74, actors=[Kristen Bell, Idina Menzel, Jonathan Groff, Josh Gad]]
  Movie [name=12 Years a Slave, genre=Drama, year=2013, lengthMinutes=134, rating=8.1, votes=486338, revenue=56.67, actors=[Chiwetel Ejiofor, Michael Kenneth Williams, Michael Fassbender, Brad Pitt]]
  Movie [name=Into the Woods, genre=Drama, year=2014, lengthMinutes=125, rating=6.0, votes=109756, revenue=128.0, actors=[Anna Kendrick, Meryl Streep, Chris Pine, Emily Blunt]]
  ----------------------------
  Only movie names :: 
  Guardians of the Galaxy
  Prometheus
  Split
  Passengers
  Frozen
  12 Years a Slave
  Into the Woods
  ----------------------------
  Movie names with its actors :: 
  Guardians of the Galaxy
  [Chris Pratt, Vin Diesel, Bradley Cooper, Zoe Saldana]
  Prometheus
  [Noomi Rapace, Logan Marshall-Green, Michael Fassbender, Charlize Theron]
  Split
  [James McAvoy, Anya Taylor-Joy, Haley Lu Richardson, Jessica Sula]
  Passengers
  [Jennifer Lawrence, Chris Pratt, Michael Sheen, Laurence Fishburne]
  Frozen
  [Kristen Bell, Idina Menzel, Jonathan Groff, Josh Gad]
  12 Years a Slave
  [Chiwetel Ejiofor, Michael Kenneth Williams, Michael Fassbender, Brad Pitt]
  Into the Woods
  [Anna Kendrick, Meryl Streep, Chris Pine, Emily Blunt]
  ----------------------------
  Movies with specific genre :: 
  Movie [name=12 Years a Slave, genre=Drama, year=2013, lengthMinutes=134, rating=8.1, votes=486338, revenue=56.67, actors=[Chiwetel Ejiofor, Michael Kenneth Williams, Michael Fassbender, Brad Pitt]]
  Movie [name=Into the Woods, genre=Drama, year=2014, lengthMinutes=125, rating=6.0, votes=109756, revenue=128.0, actors=[Anna Kendrick, Meryl Streep, Chris Pine, Emily Blunt]]
  ----------------------------
  Movies with a particular genre and rating :: 
  12 Years a Slave
  [Chiwetel Ejiofor, Michael Kenneth Williams, Michael Fassbender, Brad Pitt]
  ----------------------------
  
The accept() method operates on the given argument. The andThen() method returns a Consumer that performs this operation first, followed by the after operation.

BiConsumer Interface

The BiConsumer interface is included in the java.util.function package. The BiConsumer interface has been available since Java 8. It is used to implement functional programming in Java. It represents a function that takes two arguments and returns a result. It is similar to the Consumer interface, with the exception that it takes two arguments. Consider the following example:
import java.util.function.BiConsumer;

public class BiConsumerExample {
    public static void main(String[] args) {
        // printing two numbers
        BiConsumer<Integer, Integer> simpleBiConsumer = 
                        (x, y) -> System.out.println(x + ", " + y);
        simpleBiConsumer.accept(23, 32);

        // sum of two numbers
        BiConsumer<Integer, Integer> mathBiConsumer = 
                        (x, y) -> System.out.println("Sum :: " + (x + y));
        mathBiConsumer.accept(23, 32);
        
        System.out.println("-------------------------------");

        List<Movie> movieList = MovieList.getMovieList();
        
        System.out.println("Movie name with year :: ");
        BiConsumer<String, String> movieBiConsumer = (name, year) -> System.out.println(name + " (" + year + ")");
        movieList.forEach(movie -> {
            movieBiConsumer.accept(movie.getName(), movie.getYear());
        });
    }
}
Output

  23, 32
  Sum :: 55
  -------------------------------
  Movie name with year :: 
  Guardians of the Galaxy (2014)
  Prometheus (2012)
  Split (2016)
  Passengers (2016)
  Frozen (2013)
  12 Years a Slave (2013)
  Into the Woods (2014)
  

Predicate Interface

Predicate is Java 8 functional interfaces. A predicate is a function with a single argument that returns true or false. The test method of Predicate returns true or false. Predicate is a simple statement that determines whether the opinions are true or false based on arguments. It has the following functional methods: test(), and(), or() and negate(). Consider the following example:
import java.util.function.Predicate;

public class PredicateExample {
    public static void main(String[] args) {
        Predicate<Integer> greaterThan = (x) -> x > 30;
        System.out.println("Is greater than : " + greaterThan.test(100));

        Predicate<Integer> checkEven = (x) -> x % 2 == 0;
        
        System.out.println("Is even : " + checkEven.test(60));
        System.out.println("Negate : " + checkEven.negate().test(60));
        
        // both the conditions are true
        System.out.println("Chaining with and : " + greaterThan.and(checkEven).test(40));

        // any one condition is true
        System.out.println("Chaining with or : " + greaterThan.or(checkEven).test(25));

        // both the conditions are !true
        System.out.println("Chaining with negate : " + greaterThan.and(checkEven.negate()).test(28));
        
        System.out.println("-------------------------------");
        
        List<Movie> movieList = MovieList.getMovieList();

        Predicate<Movie> checkRating = (m) -> m.getRating() >= 7;
        Predicate<Movie> checkRevenue = (m) -> m.getRevenue() > 130;
        Predicate<Movie> checkGenre = (m) -> m.getGenre().equals("Drama");

        // movies with rating > 7 && revenue > 130
        movieList.forEach(m -> {
            if (checkRating.and(checkRevenue).test(m)) {
                System.out.println(m);
            }
        });

        System.out.println("-------------------------------");
        // movies with genre = 'Drama' && rating > 7
        movieList.forEach(m -> {
            if (checkGenre.and(checkRating).test(m)) {
                System.out.println(m);
            }
        });

        System.out.println("-------------------------------");
        // movies with genre = 'Drama' && !rating > 7
        movieList.forEach(m -> {
            if (checkGenre.and(checkRating.negate()).test(m)) {
                System.out.println(m);
            }
        });
    }
}
Output

  Is greater than : true
  Is even : true
  Negate : false
  Chaining with and : true
  Chaining with or : false
  Chaining with negate : false
  -------------------------------
  Movie [name=Guardians of the Galaxy, genre=Sci-Fi, year=2014, lengthMinutes=121, rating=8.1, votes=757074, revenue=333.13, actors=[Chris Pratt, Vin Diesel, Bradley Cooper, Zoe Saldana]]
  Movie [name=Split, genre=Horror, year=2016, lengthMinutes=117, rating=7.3, votes=157606, revenue=138.12, actors=[James McAvoy, Anya Taylor-Joy, Haley Lu Richardson, Jessica Sula]]
  Movie [name=Frozen, genre=Animation, year=2013, lengthMinutes=102, rating=7.5, votes=451894, revenue=400.74, actors=[Kristen Bell, Idina Menzel, Jonathan Groff, Josh Gad]]
  -------------------------------
  Movie [name=12 Years a Slave, genre=Drama, year=2013, lengthMinutes=134, rating=8.1, votes=486338, revenue=56.67, actors=[Chiwetel Ejiofor, Michael Kenneth Williams, Michael Fassbender, Brad Pitt]]
  -------------------------------
  Movie [name=Into the Woods, genre=Drama, year=2014, lengthMinutes=125, rating=6.0, votes=109756, revenue=128.0, actors=[Anna Kendrick, Meryl Streep, Chris Pine, Emily Blunt]]
  
The test() method evaluates the predicate on the given argument. The and() method returns a composed predicate representing a logical AND of this predicate and another. or() method returns a composed predicate representing a short-circuiting logical OR of this and another predicate. The negate() method returns a predicate representing the logical inverse of this predicate.

BiPredicate Interface

BiPredicate is a functional interface in Java 8. It is similar to the Predicate interface except that it accepts two arguments. Consider the following example:
public class BiPredicateExample {
    public static void main(String[] args) {
        List<Movie> movieList = MovieList.getMovieList();

        // bipredicate with rating >= 7 && revenue > 130
        BiPredicate<Double, Double> biPredicate = (rating, revenue) -> rating >= 7 && revenue > 130;

        // biconsumer to print movie name and its actors
        BiConsumer<String, List<String>> biConsumer = (name, actors) -> {
            System.out.println("Movie name : " + name);
            System.out.println("Actors : " + actors);
        };

        movieList.forEach(m -> {
            if (biPredicate.test(m.getRating(), m.getRevenue())) {
                biConsumer.accept(m.getName(), m.getActors());
                System.out.println("--------------------------------");
            }
        });
    }
}
Output

  Movie name : Guardians of the Galaxy
  Actors : [Chris Pratt, Vin Diesel, Bradley Cooper, Zoe Saldana]
  -------------------------------- 
  Movie name : Split Actors : [James McAvoy, Anya
  Taylor-Joy, Haley Lu Richardson, Jessica Sula] 
  --------------------------------
  Movie name : Frozen 
  Actors : [Kristen Bell, Idina Menzel, Jonathan Groff, Josh Gad]
  --------------------------------
  

Happy coding!!! 😊
in

2 comments:

  1. More examples like Movie list

    ReplyDelete
  2. However, I don't understand why the lambda expression only allows pure functions. Is there a way to do impure operations in lambda?.. more about this topic please.

    ReplyDelete

Popular posts