Simplify Your Java Code With Functional Programming: Quick overview

Simplify Your Java Code With Functional Programming: Quick overview

You may have heard about functional programming, it's a programming paradigm like many others. Functional programming is a style of programming that helps us to implement some tasks in a really easy and robust way.

Lambda Expressions

Lambda expressions were added to Java in 2014 together with Java 8 version. Simply put, a Java lambda expression is a function which can be created without belonging to any class and express an instance of a functional interface.

Functional Interface

Basically, in Java a functional interface is an interface with only a single abstract method. There many functional interfaces in Java World like Runnable, ActionListener, Comparable.

Since Java 8 we have a package called java.util.function which provides some predefined functional interfaces to help us write common tasks.

In this article I'm going to present the following 4 types of functional interfaces:

  • Consumer Interface
  • Supplier Interface
  • Function
  • Predicate


Consumer Interface

A consumer interface represents an operation that takes only one argument and doesn't return any value. There are a lot of methods that expect a consumer interface. Below you can see in the official documentation that it has only one abstract method called accept(T t) with no returned value.

Não foi fornecido texto alternativo para esta imagem

Usage

Here is an example of how we can make the use of a consumer interface to iterate over a list of elements and to turn your code into a declarative style.

Imperative style without consumer interface

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4);

    for (Integer n : numbers) {
        System.out.println(n);
    }
}

Declarative style using consumer interface

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4);

    numbers.forEach(n -> System.out.println(n));
}

The forEach method has a consumer function as a parameter, so we can simply pass an anonymous function as an argument.


Supplier Interface

The supplier interface is the opposite of the consumer interface, since it supplies a value instead of consume a value. Below you can see in the official documentation that it has only one abstract method called get() which returns a value of type T.

Não foi fornecido texto alternativo para esta imagem

Usage

Here is an example of how we can make the use of a supplier interface to generate a random value. The supplier interface also has few specializations for work with primitive values, as you can see below, it's better to use these specializations whether working with primitive values to avoid paying the cost of autoboxing/unboxing.

Using Generic Supplier Interface

public static void main(String[] args) {
    Supplier<Double> getRandom = () -> Math.random();

    System.out.println(getRandom.get());
}

Using DoubleSupplier Interface

public static void main(String[] args) {
    DoubleSupplier getRandom = () -> Math.random();
    
    System.out.println(getRandom.getAsDouble());
}


Function

A Function interface represents a function with just one argument and returns a value. Below you can see in the official documentation that it has only one abstract method called apply(T t) which takes a value of type T as an argument and returns a value of type R. It's also has a method called andThen() that takes a Function as an argument and return a Function as result, using this method we can split our code into small pieces and then combine them together.

Não foi fornecido texto alternativo para esta imagem

Usage

Here is an example of how we can make the use of a Function interface to pass an Integer as an argument and return its half as a Double value, then we combine the result with another Function that takes a Double as an argument and adds 10, then return as Double value.

Using Function Interface combined

public static void main(String[] args) {
    Function<Integer, Double> half = number -> number / 2.0;
    Function<Double, Double> add = number -> number + 10;

    System.out.println(half
                        .andThen(add)
                        .apply(10));
}

//output: 15.0


Predicate

A predicate interface represents a function with just one argument and returns a boolean value. Predicate has thousands of applications, for example, we can use it to filter some data. Below you can see in the official documentation that it has only one abstract method called test(T t) which takes an object of type T as an argument and returns a boolean. It also has more two methods called and(Predicate<? super T> other) and or(Predicate<? super T> other) that takes a Predicate as an argument, so we can use it to combine and build more complex predicates.

Não foi fornecido texto alternativo para esta imagem

Usage

Here is an example of how we can make the use of a Predicate Interface to check whether a number is greater than 18 and then check if its lower than 30.

Using Predicate Interface

public static void main(String[] args) {
    Predicate<Integer> isGraterThan18 = number -> number > 18;
    Predicate<Integer> isLowerThan30 = number -> number < 30;

    Predicate<Integer> isGranterThan18AndLowerThan10 = isGraterThan18
                                                            .and(isLowerThan30);

    System.out.println(isGranterThan18AndLowerThan10
                                                .test(20));
}

//output: true


Conclusion

In this article, I tried to present 4 different functional interfaces in Java 8 that can be used as lambda expressions and brings a lot of flexibility and readability to our code. We can use the power of functional interfaces in different situations in real-world applications.

There are more Functional interfaces in the package java.util.function to you to explore and have fun. Check it out

To view or add a comment, sign in

Others also viewed

Explore content categories