Functional Programming and Lambda Expressions

Lesson -303.14 - Functional Interface and Lambda Expressions

Functional Interface and Lambda Expression

Learning Objectives: In this lesson, we will discuss and demonstrate functional interface and Lambda expressions. By the end of this session, learners will be able to:

  • Explain functional interfaces.

  • Demonstrate how to create and use functional interfaces.

  • Demonstrate how to implement functional interfaces.

  • Describe Lambda Expressions.

  • Demonstrate how to declare Lambda Expressions.

  • Describe built-in Lambda Expressions.

Table of Contents

  • Topic 1: Introduction to Functional Programming.

  • Topic 2a: Java Functional Interface.

  • Topic 2b: Instantiate Functional Interface.

  • Topic 2c: Generic Functional Abstraction.

  • Topic 3: Shorthand Notations for Functional Interface.

  • Topic 3a: Introduction to Lambda Expressions.

  • Topic 4: Built-in Functional Interfaces in Java.

  • Topic 4a: Functional Interface - Consumer<E>.

  • Topic 4b: Functional Interface - Supplier<E>.

  • Topic 4c: Functional Interface - Predicate<E>.

  • Topic 4d: Function interface - Functional<T,R>.

Topic 1: Introduction to Functional Programming

Functional Programming is a programming paradigm in which we try to bind everything in pure mathematical functions style. It is a declarative type of programming style. Functional Programming is also called declarative type programming style, and uses expressions instead of statements.

Topic 2a: Java Functional Interface

Each Functional Interface has a single abstract method (SAM) called the functional method. An interface that contains exactly a single abstract method means that the interface implementation will only represent one behavior. Apart from one abstract method, a functional interface can also have the following methods that do not count for defining it as a functional interface.

  • Default methods.

  • Static methods.

  • Public methods inherited from the Object Class. Conceptually, a functional interface has exactly one abstract method. Since default methods have an implementation, they are not abstract. You are free to add as many default methods and static methods as you like to your functional interface. In addition, a functional interface can also specify an Object's Class public methods, such as toString(), equal(), and others. Java 8 has a new package “java.util.function” that contains many functional interfaces.

Topic 2b: Instantiate Functional Interface

We can instantiate interfaces anonymously. The Anonymous class is a new “nameless” class that implements the interface and is instantiated anonymously. These kinds of interfaces are functional interfaces. Java 8 introduces the annotation @FunctionalInterface. The annotation is used to ensure that the functional interface cannot have more than one abstract method. If more than one abstract method is present, the compiler flags an error. However, it is not mandatory to use this annotation. If we remove the annotation, we are allowed to add another abstract method, but it will make the interface non-functional. In Java 8, functional interfaces can be represented using anonymous classes, Lambda expressions, method references, and constructor references.

Example 1: Functional Interface

Let’s build our first functional interface:

@FunctionalInterface
public interface MyFirstFunctionalInterface {
    public void firstWork();
}

For the testing purpose, run your code. Your code should execute without any errors.

Example 1: Functional Interface (continued)

Let’s try to add another abstract method by the name of doSomeMoreWork();

@FunctionalInterface
public interface MyFirstFunctionalInterface {
    public void firstWork();
    public void doSomeMoreWork();   //error
}

Now run you code again. You will get a compiler error in your console as shown below:

Unexpected @FunctionalInterface annotation
@FunctionalInterface^
MyFirstFunctionalInterface is not a functional interface
multiple non-overriding abstract methods found in interface MyFirstFunctionalInterface

Example 2: - Functional Interface - Anonymous Implementation Class

Step 1: Create an Interface named Human and add only one abstract method as shown below. This will be our Functional Interface.

@FunctionalInterface
public interface Human {
    void say(); /* A Single-Abstract-Method */
}

Step 2: Create a regular class named NormalClass, and write the code below:

public class NormalClass {
    Human hObjone = new Human() {
        @Override
        public void say() {
            System.out.println("I am James");
        }
    };
    Human hObjTwo = new Human() {
        @Override
        public void say() {
            System.out.println("i am Tony");
        }
    };
}

Example 3: Functional Interface

Let’s demonstrate one more example of functional interface. Step 1: Create an interface named FuncInterfaceExample and add the code below:

@FunctionalInterface
public interface FuncInterfaceExample {
    int sum(int a, int b);
    default int multiply(int a, int b) {
        return a*b;
    }
}

Step 2: Create a class named Calculator and add the code below:

public class Calculator {
    FuncInterfaceExample Fobj = new FuncInterfaceExample() {
        @Override
        public int sum(int a, int b) {
            return a + b;
        }
    };
}

Example: Generic - Functional Interface

In this example, we will demonstrate how to define a Generic Functional Interface that makes our program more dynamic.

Step 1: Create an interface named Generic_functionInterface. Specify one type parameter, as shown below code.

@FunctionalInterface
public interface Generic_functionInterface<T> {
    T compare(T a, T b);
}

Step 2: Create a class named MycomparingClass. We are giving implementation of abstract method in this class. For the demonstration, we will see two implementations of the compare() method (abstract method):

  • Compare two Integers.

  • Compare two Strings.

public class MycomparingClass {
    Generic_functionInterface<Integer> compareTwoVar = new Generic_functionInterface<Integer>() {
        public Integer compare(Integer a, Integer b) {
            if(a > b) {
                return a;
            } else {
                return b;
            }
        }
    };
    Generic_functionInterface<String> stCompare = new Generic_functionInterface<String>() {
        public String compare(String a, String b) {
            if(a.equals(b)) {
                return "true";
            } else {
                return "false";
            }
        }
    };
}

Topic 3: Shorthand Notations for Functional Interface

JDK 8 introduces lambda expressions, which provides a shorthand notation for creating an instance of an anonymous inner class, implementing a functional interface.

Hands-On Activity

Create a simple calculator that processes or manipulates two variables:

  • Create an Interface named Calc that has a single abstract method named compute(). This method takes an array of integers.

  • Create a class called: MainEntry. In this class, implement the compute() method four times by using blocks to add, subtract, multiply, and divide. Then ask the user to enter a series of numbers, and ask what they would like to do with the numbers. In the case of compute() divide, only allow for two numbers. Hint: Use Generic.

Knowledge Check

  • What is a Functional Interface in Java?

  • What is a Default Method in an Interface?

  • Is it possible to define our own Functional Interface?

  • What is the @FunctionalInterface annotation?

Topic 3a: Introduction to Lambda Expressions

  • In general programming language, a function or lambda expression is an anonymous (nameless) function that can be used to implement.

  • Lambda expressions are nameless functions given as constant values and written exactly in the place where they are needed.

  • Lambda expressions are used primarily to define inline implementations of a functional interface.

  • Lambda expressions eliminate the need of an anonymous class, and gives a very simple, yet powerful functional programming capability to Java.

Lambda expression

  • Syntax: (parameter_list) -> {method_body: define programming logic here }

  • No name: This function is anonymous so we do not care about the name.

  • The parameters list:

    • Can receive zero, one, or more parameters.

    • Can be defined explicitly or inferred.

    • Empty parentheses = empty set of parameters.

    • No need to declare the type of its parameters.

  • The body - This is the main part of the function.

    • Can contain zero or more statements.

    • Just like loops, curly brackets are not mandatory for a single line.

    • More than one line/statement requires curly braces {}.

  • So NO need to use parentheses {} for functions with only one parameter.

  • Optional return type:

    • The compiler automatically returns the value if the body has a single expression to return the value. We do not need to mention it explicitly.

    • Curly braces {} are required to indicate that the expression returns a value.

Syntax - Lambda Expressions (Examples)

  • NO parameter and NO return statement:

    • () -> {}; //No parameters, with curly parentheses/expression body. No return type.

    • () -> null

  • NO parameter with return statement:

    • () -> { return statement; }; //No parameters, with curly parentheses / expression body and return statement.

    • () -> expression

  • One parameter and NO return statement:

    • n -> System.out.print(n);

    • (n) ->{ System.out.print(n) };

    • (data type n) -> n ;

  • One parameter with return statement:

    • n -> {return n;}; or (n) -> {return n;};

    • n -> expression;

  • One parameter and return type:

    • (n) -> {return n;}; or n -> {return n;};

  • Multiple parameter and NO return statement:

    • (p, x) -> System.out.println(p + x)

    • (p, x) ->{ System.out.println(p + x); };

  • Multiple parameter with return statement:

    • (p, x) -> { return statement; }; // multiple parameter, with curly parentheses and return statement.

    • (p, x) -> { return (p+x); }; //multiple parameter, with curly parentheses and return statement.

Example 1: Lambda Expression - No Parameter

  • Create an interface named Human and add only one abstract method as shown below. This will be our Functional Interface.

@FunctionalInterface
public interface Human {
    void say(); /* A Single-Abstract-Method */
}
  • Let’s see how to give an implementation of functional interface using the Lambda Expression. Create a regular class named MyRunner.

public class MyRunner {
    public static void main(String[] args) {
        Human HMsg = () -> {
            System.out.println("Hello. It’s me again, James");
        };
        HMsg.say();
    }
}
  • Run your program. Output: Hello. It's me again, James

Example 2: Lambda Expressions - One Parameter

  • Create an interface named Human and add only one abstract method as shown below. This will be our Functional Interface.

@FunctionalInterface
public interface Human {
    void say(String message); /* A Single-Abstract-Method */
}
  • Let’s see how to give an implementation of functional interface using Lambda expression. Create a regular class named MyRunner.

public class MyRunner {
    public static void main(String[] args) {
        // Option One using: n -> System.out.print(n) // One parameter, without round bracket and curly parentheses
        Human HMsg_one = st -> System.out.println(st + " It’s me Tony");
        HMsg_one.say("Hello | ");

        // Option Two using: (n) -> System.out.print(n) //One parameter, without curly parentheses / expression body.
        Human HMsg_two = (st) -> System.out.println(st + " It’s me Tony");
        HMsg_two.say("Hello | ");

        // Option Three using: (n) -> { System.out.print(n) } //One parameter, with curly parentheses / expression body.
        Human HMsg_three = (st) -> {
            System.out.println(st + " It’s me Tony");
        };
        HMsg_three.say("Hello | ");
    }
}
  • Run your program. Output: Hello | It’s me Tony

Example 3: Lambda Expressions - One Parameter and Return Type

  • Create an interface named Human2 and add only one abstract method as shown below. This will be our Functional Interface.

@FunctionalInterface
public interface Human2 {
    String say(String message); /* A Single-Abstract-Method */
}
  • Let’s see how to give an implementation of function interface using Lambda Expression. Create a regular class named MyRunner.

public class MyRunner2 {
    public static void main(String[] args) {
        // Option One: n -> {return n;}; or (n) -> {return n;};
        Human2 Obj = (st) -> {
            String a = "Hello Tony ";
            return a + st;
        };
        System.out.println(Obj.say("How are you doing"));

        // Option Two: n -> expression;
        Human2 Obj2 = (st) -> "Hello Tony " + st;
        System.out.println(Obj2.say("How are you doing"));
    }
}
  • Run your program. Output: Hello Tony How are you doing

Example 4: Lambda Expression - Multiple Parameters

  • Create interface named multipleParameterDemo and add only one abstract method, As shown below. This will be our Function interface.

@FunctionalInterface
public interface multipleParameterDemo {
    /* A Single-Abstract-Method */
    void concatString(String a, String b, String c);
}
  • Let’s see how to give an implementation of function interface using Lambda expression. Create regular class named MyRunner and write below code.

public class MyRunner {
    public static void main(String[] args) {
        // Option One: (p, x) -> System.out.println(p + x)
        multipleParameterDemo sObj1 = (st1, st2, st3) -> System.out.println(st1 + st2 + st3);
        sObj1.concatString("hello | ", "Per Scholas ", "Students");

        // Option Two: (p, x) -> { System.out.println(p + x); };
        multipleParameterDemo sObj2 = (st1, st2, st3) -> { System.out.println(st1 + st2 + st3); };
        sObj2.concatString("hello | ", "Per Scholas ", "Students");
    }
}
  • Run your program. Output: hello | Per Scholas Students

Example 5: Lambda Expressions - Multiple Parameters and a Return Statement

  • Create interface named Calculator and add only one abstract method as shown below. This will be our Function interface.

@FunctionalInterface
public interface Calculator {
    double calculate(double a, double b, double c); // double return type and three parameters
}
  • Let’s see how to give an implementation of function interface using Lambda Expression. Create regular class named MyRunner2.

public class MyRunner2 {
    public static void main(String[] args) {
        // Option One: (p, x) -> { return (p+x); };
        Calculator addObj1 = (a, b, c) -> {
            return (a + b + c);
        };
        double resultObj1 = addObj1.calculate(10, 20, 30);
        System.out.println(resultObj1);

        // Option Two: (p, x) -> expression;
        Calculator addObj2 = (a, b, c) -> a + b + c;
        double resultObj2 = addObj2.calculate(10, 20, 30);
        System.out.println(resultObj2);
    }
}
  • Run your program. Output: 60.0

Topic 4: Built-in Functional Interfaces in Java

  • Java contains a set of functional interfaces designed for commonly occurring use cases. As such, you do not have to create your own functional interfaces for every little use case.

  • The following is a list of Java’s most commonly used built-in functional interfaces.

    • Runnable: contains only the run() method.

    • Comparable: contains only the compareTo() method.

    • ActionListener: contains only the actionPerformed() method.

    • Callable: contains only the call() method.

    • Predicate: a boolean-valued function that takes an argument and returns true or false.

    • BiPredicate: a predicate with two arguments.

    • Consumer: an operation that takes an argument, operates on it, and returns no result.

    • BiConsumer: a consumer with two arguments.

    • Supplier: a supplier that returns a value.

    • Function<T, R>: takes an argument of type T and returns a result of type R.

    • BiFunction<T, U, R>: takes two arguments of types T and U and returns a result of type R.

Topic 4a: Functional Interface - Consumer<E>

  • The Java Consumer Interface is a functional interface that represents a function that consumes a value without returning any value. A Java Consumer implementation could be printing out a value or writing it to a file or over the network, etc. Here is an example implementation of the Java Consumer interface:

    • Syntax: Consumer<Integer> consumer = (value) -> System.out.println(value);

  • Example: Create a new class. You can specify any name to the class. Write the code below:

import java.util.function.Consumer;

public class MyRunner {
    public static void main(String[] args) {
        Consumer<String> display = (x) -> {
            System.out.println(x);
        };
        display.accept("Per Scholas");
    }
}
  • Run your program. Output: Per Scholas

Topic 4b: Functional Interface - Supplier<E>

  • The Java Supplier Interface is a functional interface that represents a function that supplies a value of some sorts. The Supplier Interface can also be thought of as a factory interface.

  • Example: Create a class. You can specify any name to the class, and write code below:

import java.util.function.Supplier;

public class MyRunner {
    public static void main(String[] args) {
        // Example One
        Supplier<String> supplierObj = () -> "Per Scholas";
        System.out.println(supplierObj.get());

        // Example two
        // This function returns a random value.
        Supplier<Double> randomValue = () -> Math.random();
        // Print the random value using get()
        System.out.println(randomValue.get());
    }
}
  • Run your program. Output: Per Scholas 0.06988892309043637

Topic 4c: Functional Interface - Predicate<E>

The Java Predicate Interface, java.util.function.Predicate represents a simple function that takes a single value as parameter and returns true or false. Here is how the Functional Interface Predicate looks:

Example: Create a class. You can specify any name to the class, and write the code below:

import java.util.function.Predicate;

public class MyRunner {
  // Creating predicate
  Predicate<Integer> lesserthan = i -> (i < 10);
  // Calling Predicate method
  System.out.println(lesserthan.test(18));
}

Output:

true

Topic 4d: Function interface - Functional<T,R>

The Java Function Interface - Function<T,R> (java.util.function.Function) is one of the most central functional interfaces in Java. It represents a function (method) that takes a single parameter and returns a single value.

Function Interface - (java.util.function.Function)

public interface Function<T,R> {
    public <R> apply(T parameter);
}

Example: Function Interface - Functional<T,R> Create a class. You can specify any name to the class, and write the code below:

import java.util.function.Function;

public class MyRunner {
  public static void main(String[] args) {
    /*apply(T t) - Applies this function to the
    given argument, and then returns any type.*/
    Function<Integer, Integer> f1 = i -> i*4;
    Function<Integer, Integer> f2 = i -> i+4;
    System.out.println(f1.apply(3));
    System.out.println(f2.apply(3));
    Function<String, Integer> f3 = s -> s.length();
    System.out.println(f3.apply("Peter"));
  }
}

Output:

12
7
5

Knowledge Check

  • What is Lambda Expression in Java?

  • What are the benefits of using a Lambda Expression?

  • What is a Predicate interface?

  • What is the Consumer interface?

For Further Reading

The content referenced in the following slides will NOT be covered in the scope of this course. However, Per Scholas recommends that you explore the content for upskilling.

Stream API for Function Interface

What is Stream? A stream is a conceptually endless flow of data or elements or a sequence of elements that supports various methods for sequential and parallel aggregate operations.

Overview of Stream API The Java Stream API provides a functional approach to processing collections of objects. The Java Stream API was added in Java 8 along with several other functional programming features. Java 8 provides a new additional package for Stream API: java.util.stream.

Note: Java InputStream and Java OutputStream of Java IO is for processing streams of objects - not bytes. The InputStream and OutputStream are related to streams of bytes. The Java Stream API is not related to the bytes.

Java Stream forEach() Method

The Java Stream forEach() method is a terminal operation, which starts the internal iteration of the elements in the Stream and applies a Consumer (java.util.function.Consumer) to each element in the Stream. The forEach() method returns void. Here is an example of Java Stream forEach().

import java.util.List;
import java.util.ArrayList;

public class MyRunner {
  public static void main (String[] args) {
    List<String> names = new ArrayList<String>();
    names.add("Larry");
    names.add("Steve");
    names.add("James");
    names.add("Conan");
    names.add("Ellen");
    names.stream().forEach(name -> {
      System.out.println(name);
    });
  }
}

Output:

Larry
Steve
James
Conan
Ellen

Stream API for Function Interface

For more information about Java Streams API, visit the Wiki Document.

Summary

  • A functional interface has exactly one abstract method and can have any number of default and static methods.

  • From Java 8 and onwards, Lambda Expressions can be used to represent the instance of a functional interface.

  • A Lambda Expression is a short block of code, which takes in parameters and returns a value. Lambda expressions are similar to methods, but they do not need a name and they can be implemented right in the body of a method.

References

Last updated