Functional Interfaces in Java 8
java.util.function package was introduced in Java 8. It contains functional interfaces, which is described the following way in the API specification:
Functional interfaces provide target types for lambda expressions and method references. Each functional interface has a single abstract method, called the functional method for that functional interface, to which the lambda expression's parameter and return types are matched or adapted.
In this post I'm quickly going through 4 out of the over 40 interfaces in this package.
Represents a predicate (boolean-valued function) of one argument.
The predicate interface allows us to create lambda expressions that return a boolean value based on the argument given. Let's create a predicate that tests if a person is defined as an adult.
Predicate<Integer> isAnAdult = age -> age >= 18;
The new filter method used for streams takes the Predicate interface as an argument. So we can actually try using our new predicate in a stream.
Predicate<Person> isAnAdult = person -> person.getAge() >= 18; List<Person> people = getAllPeople(); Integer nrOfAdults = people.stream() .filter(isAnAdult) .count();
Represents an operation that accepts a single input argument and returns no result. Unlike most other functional interfaces, Consumer is expected to operate via side-effects.
Consumer<Ticket> ticketPrinter = ticket -> ticket.print();
The new forEach method in the Iterable interface takes the Consumer interface as an argument. Let's try to combine the ticketPrinter operation we created above on a Collection using the forEach method:
Consumer<Ticket> ticketPrinter = ticket -> ticket.print(); Collection<Ticket> tickets = getTicketsToPrint(); tickets.forEach(ticketPrinter);
Now, let's clean it up by putting the Consumer straight into the forEach method.
Collection<Ticket> tickets = getTicketsToPrint(); tickets.forEach(ticket -> ticket.print());
Represents a supplier of results.
This is kind of a factory. It takes no arguments, and just gives you a result. Perfect for returning an instance.
Supplier<TicketHandler> ticketHandlerCreator = () -> new TicketHandler();
Another solution is to use the constructor reference.
Supplier<TicketHandler> ticketHandlerCreator = TicketHandler::new;
Time to look at the last interface in this post!
Represents a function that accepts one argument and produces a result.
Let's just go straight to an example.
Function<String, Predicate<Ticket>> ticketFor = event -> ticket -> event.equals(ticket.getName()); List<Ticket> tickets = getAllTickets(); Integer soldTicketsForCoolEvent = tickets.stream() .filter(ticketFor.apply("CoolEvent")) .count();
Here we create a function that takes an event name as an argument, then returns a predicate. Arguments given to the predicate, will be compared against this event name. Then we used the function in a stream that counted the number of sold tickets for the "CoolEvent".