JavaFunctional streams

Standard functional interfaces

Kind of functional interfaces

Since Java 8 the JCL has a lot of functional interfaces for representing functions. These functional interfaces are in the java.util.function package.

The package contains more than 40 functional interfaces of five groups: functions, operators, predicates, suppliers, and consumers.

Let's consider these groups:
  • functions accept arguments and produce results.
  • operators produce results of the same type as their arguments (a special case of function).
  • predicates return boolean values and accept any arguments (boolean-valued function).
  • suppliers return values and accept nothing.
  • consumers accept arguments and return no result.
Main differences between functional interfaces in the same group are the number of arguments and generic or not.

Some examples:
  • Function<T, R> accepts a value of type T and produces a result of type R.
  • BiFunction<T, U, R> accepts two values of T and U types and produces a result of type R.
  • LongFunction<R> accepts a long value and produces a result of type R.
  • IntToDoubleFunction accepts an integer value and produces a double value.
  • IntPredicate accepts an integer value and returns boolean value.
  • Supplier<T> returns a value of type T.
  • BiConsumer<T, U> accepts two values of T and U types.
  • and others...


Sometimes we will use generic interfaces and sometimes non-generic.

Learning standard functions interfaces with examples

Let's create some lambda expressions for standard functional interfaces. Note, some functional interfaces takes or returns values of primitive types: int, long or double.

1) Functions
// String to Integer function
Function<String, Integer> converter = Integer::parseInt;
converter.apply("1000"); // the result is 1000 (Integer)

// String to int function
ToIntFunction<String> anotherConverter = Integer::parseInt;
anotherConverter.applyAsInt("2000"); // the result is 2000 (int)

// (Integer, Integer) to Integer function
BiFunction<Integer, Integer, Integer> sumFunction = (a, b) -> a + b;
sumFunction.apply(2, 3); // it returns 5 (Integer)

Also, there are a lot of functions: IntFunction<R>, LongFunction<R>, DoubleFunction<R>, ToLongFunction<T>, LongToDoubleFunction, ToIntBiFunction<T, U> and so on.

Note, there are no standard functions that take more than two arguments.

2) Operators

// int to int operator
IntUnaryOperator intMultiplier = val -> 100 * val;
intMultiplier.applyAsInt(10); // the result is 1000 (int)

// Long to Long multiplier
UnaryOperator<Long> longMultiplier = val -> 100_000 * val;
intMultiplier.apply(2); // the result is 200_000L (Long)
        
// (String, String) to String operator
BinaryOperator<String> appender = (str1, str2) -> str1 + str2;
appender.apply("str1", "str2"); // the result is "str1str2"

Also, there are other operators: IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator, IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator.

Any operator takes and returns values of the same type.

3) Predicates

// int to boolean predicate
IntPredicate isEven = val -> val % 2 == 0;
isEven.test(10); // the result is true (boolean)

// Character to boolean predicate
Predicate<Character> isDigit = Character::isDigit;
isDigit.test('h'); // the result is false (boolean)

Also, there are other predicates: LongPredicate, DoublePredicate, BiPredicate<T, U>.

Any predicate always returns the value of the boolean type (primitive).

4) Suppliers

Supplier<String> stringSupplier = () -> "Hello";
stringSupplier.get(); // the result is "Hello" (String)

BooleanSupplier booleanSupplier = () -> true;
booleanSupplier.getAsBoolean(); // the result is true (boolean)

IntSupplier intSupplier = () -> 33;
intSupplier.getAsInt(); // the result is 33 (int)

Also, there are other suppliers: LongSupplier, DoubleSupplier.

Any supplier always doesn't take any arguments and returns a value.

5) Consumers

// it prints a given string
Consumer<String> printer = System.out::println;
printer.accept("!!!"); // It prints "!!!"

Also, there are other consumers: IntConsumer, LongConsumer, DoubleCounsumer, BiConsumer<T, U>, ObjIntConsumer<T>, ObjLongConsumer<T>, ObjDoubleConsumer<T>.

Any consumer doesn't return a value.

Summing up

As you can see, types of interfaces (left) and type of lambdas (right) has the same semantic meaning.

Actually, parameters and returned value of a lambda expression (or a method reference) correspond to parameters and returned value of a single abstract method in a functional interface.

For example:
  • IntSupplier has a method int getAsInt() that corresponds to lambda () -> 33 in the example above.
  • IntPredicate has a method boolean test(int value) that corresponds to lambda val -> val % 2 == 0.
Further, we can use generic and primitive-specialized standard functional interfaces together.
How did you like the theory?
Report a typo