Function Functional Interface in Java

In this lesson, we will explore the Function interface from java.util.function package.
It represents a function that takes in one argument and produces a result.

It has one functional (single abstract) method R apply(T t), which takes one input of type T and returns the function result of type R.
It also contains two default and one static method.

Since it is a Functional Interface, we can implement it with a Lambda expression.

Function<T, R>

@FunctionalInterface
public interface Function<T, R> {
    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

    /**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /**
     * Returns a function that always returns its input argument.
     *
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}


T represents the type of the input parameter and U represents the return type.


Implementing the Function interface in Java

Example 1:
Implementation of the single abstract method R apply(T t)

class Test {

  public static void main(String[] args) {
    Function<String, String> f1 = str -> str.concat("Programming");
    System.out.println(f1.apply("Java"));
  }
}

Output: JavaProgramming


Here, inside the braces <>, we specified what input and output types, and we used a Lambda expression to write the implementation of the Function interface. 
On the left side of the arrow (->) sign, we specified the input and on the right side, we wrote the method’s logic.

NoteIf we have a single statement inside the Lambda body, then we don’t need to put the curly braces {}.
The same for the input parameter, if we are dealing with only one, then we don’t need to put it between the parentheses ().
Also, if we have a single statement that returns some result, we don’t have to put the return keyword, it would be redundant.


Example 2:
Implementation of the <V> Function<T, V> andThen(Function<? super R, ? extends V> after) method that accepts the Function and returns the Function also.

class Test {

  public static void main(String[] args) {
    Function<String, String> concatStrings = str -> str.concat("Programming");
    Function<String, String> toUpperCase = str -> str.toUpperCase();

    System.out.println(concatStrings.andThen(toUpperCase).apply("Java"));
  }
}

Output: JAVAPROGRAMMING


As you can see, we used the andThen() method to chain method calls. First, the concatStrings() got executed, followed by a call to the toUpperCase() method.
The andThen() needs to be followed by a call to the apply() method so that we can pass the parameter.

Example 3:

Implementing the <V> Function<V, R> compose(Function<? super V, ? extends T> before) method that accept the Function and return the Function also. Just like the andThen() method. 

class Test {

  public static void main(String[] args) {
    Function<String, String> concatStrings = str -> str.concat("Programming");
    Function<String, String> toUpperCase = str -> str.toUpperCase();

    System.out.println(concatStrings.compose(toUpperCase).apply("Java"));
  }
}

Output: JAVAProgramming


Here, we used the same example as for the andThen() method. Could you see the difference in the output?
It’s not the same because with the compose() method, the function that is passed as a parameter will be executed first, which is in this case the toUpperCase() method, and then the concatStrings() will be called.

Example 4:
Implementing the <T> Function<T, T> identity() method that accepts no input and returns a function which returns its own argument.

class Test {

  public static void main(String[] args) {
    Function<String, String> function = Function.identity();
    System.out.println(function.apply("Hello Java!"));
  }
}

Output: Hello Java!


As you can see, the identity() function returned the same input parameter that we passed in the apply() method.

Happy coding!

 

Leave a Reply

Your email address will not be published.