Lambda Built-in Functional Interfaces

Use the built-in interfaces included in the java.util.function package such as Predicate, Consumer, Function, and Supplier

Functional interfaces provide target types for lambda expressions and method references. Each functional interface has a single abstract method, called functional method for that functional interface, to which the lambda expression's parameter and return types are matched or adapted.

Predicate

A predicate is a statement that may be true or false depending on the values of its variables. It can be thought of as a function that returns a value that is either true or false.

In Java 8, a Predicate is a functional interface that can be used anywhere you need to evaluate a boolean condition. Since it's a functional interface, you can pass a lambda expression wherever a Predicate is expected.

See the API to know the methods of this interface.

Here's an example. First, we see how the interface with an anonymous class:

Predicate<String> isALongWord = new Predicate<String>() {
    @Override
    public boolean test(String t) {
        return t.length() > 10;
    }
};
String s = "successfully"
boolean result = isALongWord.test(s);

And now with a lambda expression:

Predicate<String> isALongWord = t -> t.length() > 10;
String s = "successfully"
boolean result = isALongWord.test(s);

Predicates are also used to filter collections, for example:

public class Test {
  public static void main(String[] args) {
    List<String> l = new ArrayList<>();
    l.add("successfully");
    l.add("easy");
    l.add("fortune");
    List<String> filtered = l.stream().filter( s -> s.length() > 5 ).collect(Collectors.<String>toList());
    System.out.println(filtered);
  }
}

Here, the filter method expects a Predicate, so we can pass a lambda expression to simplify things, so the output of the example is:

["successfully", "fortune"]

Consumer

This functional interface represents an operation that accepts a single input argument and returns no result. The real outcome is the side-effects it produces. Since it's a functional interface, you can pass a lambda expression wherever a Consumer is expected.

See the API to know the methods of this interface.

Here's an example:

class Product {
  private double price = 0.0;

  public void setPrice(double price) {
    this.price = price;
  }

  public void printPrice() {
    System.out.println(price);
  }
}

public class Test {
  public static void main(String[] args) {
    Consumer<Product> updatePrice = p -> p.setPrice(5.9);
    Product p = new Product();
    updatePrice.accept(p);
    p.printPrice();
  }
}

Basically, what Consumer does is executing the assigned lambda expression. The side-effect here, it's the updating of the product's price, so the output is:

5.9

Function

This functional interface represents a function that accepts one argument and produces a result. One use, for example, it's to convert or transform from one object to another. Since it's a functional interface, you can pass a lambda expression wherever a Function is expected.

See the API to know the methods of this interface.

Here's an example:

public class Test {
  public static void main(String[] args) {
    int n = 5;
    modifyTheValue(n, val-> val + 10);
    modifyTheValue(n, val-> val * 100);
  }

  static void modifyValue(int v, Function<Integer, Integer> function){
    int result = function.apply(v);
    System.out.println(newValue);
  }

}

The input parameter type and the return type of the method can either be same or different. In this case, they are the same type and the program just execute the functions represented by the lambda expression, an addition and a multiplication, so the output is:

15
500

Supplier

This functional interface does the opposite of the Consumer, it takes no arguments but it returns some value. It may return different values when it is being called more than once. Since it's a functional interface, you can pass a lambda expression wherever a Supplier is expected.

See the API to know the one method of this interface.

Here's an example:

public class Program {
    public static void main(String[] args) {
        int n = 3;
        display(() -> n + 10);
        display(() -> n + 100);
    }

    static void display(Supplier<Integer> arg) {
        System.out.println(arg.get());
    }
}

Basically, a Supplier just provides values. The output of the example is:

13
103

 

Develop code that uses primitive versions of functional interfaces

Due to the way generics are implemented, parameters of the functional interfaces (for example, Predicate<T>) can be bound only to reference types (like String, objects, etc).

If you want to use primitive types with these functional interfaces, Java uses a mechanism called autoboxing to automatically convert a primitive to its corresponding wrapper type (for example, int to Integer) and vice versa.

But since boxed values use more memory, this comes with a performance cost. For this reason, Java provides specialized versions of the functional interfaces to avoid autoboxing operations when the inputs or outputs are primitives.

For example, instead of using

Predicate<Integer> p = i -> i > 10;

You can use

IntPredicate p = i -> i > 10;

In general, the names of functional interfaces that have a primitive version for the input parameter are preceded by the primitive type, like IntPredicate. The Function interface also has variants for the output parameter like ToIntFunction<T>.

Here's a summary of the primitive version of functional interfaces with a link to their javadoc:

Predicate<T>
IntPredicate. Predicate of one int-valued argument.
LongPredicate. Predicate of one long-valued argument.
DoublePredicate. Predicate of one double-valued argument.

Consumer<T>
IntConsumer. Operation that accepts a single int-valued argument and returns no result.
LongConsumer. Operation that accepts a single long-valued argument and returns no result.
DoubleConsumer. Operation that accepts a single double-valued argument and returns no result.

Function<T, R>
IntFunction<R>. Function that accepts an int-valued argument and produces a result.
IntToDoubleFunction. Function that accepts an int-valued argument and produces a double-valued result.
IntToLongFunction. Function that accepts an int-valued argument and produces a long-valued result.
LongFunction<R>. Function that accepts a long-valued argument and produces a result.
LongToDoubleFunction. Function that accepts a long-valued argument and produces a double-valued result.
LongToIntFunction. Function that accepts a long-valued argument and produces an int-valued result.
DoubleFunction<R>. Function that accepts a double-valued argument and produces a result.
ToIntFunction<T>. Function that produces an int-valued result.
ToDoubleFunction<T>. Function that produces a double-valued result.
ToLongFunction<T>. Function that produces a long-valued result.

Supplier<T>
BooleanSupplier. Supplier of boolean-valued results.
IntSupplier. Supplier of int-valued results.
LongSupplier. Supplier of long-valued results.
DoubleSupplier. Supplier of double-valued results.

UnaryOperator<T>
IntUnaryOperator. Function operation on a single int-valued operand that produces an int-valued result.
LongUnaryOperator. Function operation on a single long-valued operand that produces a long-valued result.
DoubleUnaryOperator. Function operation on a single double-valued operand that produces a double-valued result.

 

Develop code that uses binary versions of functional interfaces

The following functional interfaces:

  • Predicate<T>
  • Consumer<T>
  • Function<T,R>
  • UnaryOperator<T>

Represent an operation that takes one argument. But there are versions of these interfaces that take two arguments called. These are the binary versions. They have the same semantics, the only difference is the number of arguments. Note there is no binary version of Supplier. This is because a Supplier takes no arguments.

Here's a summary of the binary versions of the functional interfaces along with their primitive versions and a link to their javadoc:

BiPredicate<L, R>
(No primitive versions)

BiConsumer<T, U>
ObjIntConsumer<T>. Operation that accepts an Object-valued and an int-valued argument and returns no result.
ObjLongConsumer<T>. Operation that accepts an Object-valued and a long-valued argument and returns no result.
ObjDoubleConsumer<T>. Operation that accepts an Object-valued and a double-valued argument and returns no result.

BiFunction<T, U, R>
ToIntBiFunction<T, U>. Function that accepts two arguments and produces an int-valued result.
ToLongBiFunction<T, U>. Function that accepts two arguments and produces a long-valued result.
ToDoubleBiFunction<T, U>. Function that accepts two arguments and produces a double-valued result.

BinaryOperator<T>
IntBinaryOperator. Function operation upon two int-valued operands and producing an int-valued result.
LongBinaryOperator. Function operation upon two long-valued operands and producing a long-valued result.
DoubleBinaryOperator. Function operation upon two double-valued operands and producing a double-valued result.

 

Develop code that uses the UnaryOperator interface

UnaryOperator is a functional interface that receives a value of a certain type and returns a value of the same type. This is a specialization of the Function interface for the case where the operand and result are of the same type (in fact UnaryOperator extends from Function).

Here's the javadoc.

And here's an example:

public class Test {
  public static void main(String[] args) {

      UnaryOperator<Integer> unary = v -> v * 10;
      // This means the same as the UnaryOperator above.
      Function<Integer, Integer> function = v -> v * 10;

      System.out.println(unary.apply(10));
      System.out.println(function.apply(10));
  }
}

The output:

100
100

The UnaryOperator can also be applied to a collection like this:

public class Program {
  public static void main(String[] args) {
      List<Integer> list = new ArrayList<>();
      list.add(1);
      list.add(2);
      list.add(3);
      list.replaceAll(i -> i * 10);
      // ... Display the results.
      System.out.println(list);
  }
}

The output:

[10, 20, 30]

 

Content