In the previous post we saw an overview of what functional programming is and how the new features of Java 8 allow developers to write their applications using a more functional style. One of the main points in this new version of the language was the introduction of lambdas. Together with lambdas came the use of functional interfaces and methods references. This post will explore these features in more detail, showing when to use them, the restrictions around them and how you can use them to make your code more readable and concise.


First things first, what is a lambda (or lambda expression)? A lambda is an anonymous method that doesn’t have a name but it has a list of parameters, a body, a return type and potentially a list of exception that the lambda can throw. Unlike regular class methods, lambdas are not actually associated with any class. They can also be assigned to variables or passed as arguments to other methods. The name lambda expression comes from the field of mathematics.

We saw an example of a lambda expression in the previous post, using an example from the File class to list csv files:

File[] csvFiles = new File(".")
                    .listFiles(pathname -> pathname.getAbsolutePath().endsWith("csv"));

Here, we are passing a lambda expression to the listFiles method that takes one input parameter and returns a boolean value. I also mentioned that you can assign lambdas to variables, so the previous code is functionally equivalent to:

FileFilter csvFilter = pathname -> pathname.getAbsolutePath().endsWith("csv");
File[] csvFiles = new File(".").listFiles(csvFilter);

How did we use to do that before Java 8? Like this:

File[] csvFiles = new File(".").listFiles(new FileFilter() {
    public boolean accept(File pathname) {
      return pathname.getAbsolutePath().endsWith("csv");

You have to admit that the snippet using a lambda expression looks much more concise and cleaner. In the last snippet we have to create an anonymous class with an accept method (with all the verbosity that it implies). In the first one, we just need to specify our logic.

This brings an interesting question, if the listFiles method takes a parameter of FileFilter type (which is an interface), how come we can pass a lambda instead? We can do this because the FileFilter interface is a functional interface.

Functional interfaces

In a nutshell, a functional interface is an interface that specifies exactly one abstract method. So the FileFilter interface we saw before is specified as:

public interface FileFilter {
  boolean accept(File pathname);

Another example, is the Runnable interface:

public interface Runnable {
  public abstract void run();

You can see that both of these interfaces have a @FunctionalInterface annotation. So what does that do? First it informs people who look at that interface that it is intended to be a functional interface and that they can use lambdas and method references wherever they are expected. Second, it works as a compile-time check to make sure that the interface is indeed functional. If you add this annotation to your interface and it is not in fact functional then you will get a nice compile error letting you know this. It is worth noting that the annotation is not actually required but it is usually a good idea to have it there for the reasons I mentioned before.

There’s one small caveat here. We briefly discussed default methods in the previous post, which are methods whose implementation code can be written in an interface. Default methods do not count for the “exactly one abstract method” rule of functional interfaces so you can effectively have a functional interface with one abstract method and one or more default methods.

Some useful functional interfaces

Now that we know what a functional interface is and how it can be used, lets look at some pretty useful interfaces provided by Java in its java.function.util package


The predicate interface defines a simple test method that takes an object and returns a boolean. It looks something like:

public interface Predicate<T> {
    boolean test(T t);

This is pretty useful for things like filtering. You could have a generic method to filter a list (this is just an example, you don’t need to write this logic and we’ll see why when we go into streams):

public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
  List<T> result = new ArrayList<>();
  for (T elem : list) {
    if (predicate.test(elem)) {
  return result;

And then create a predicate for your particular object. For example, a predicate that given a User returns true if his age is greater than or equal to 18:

public enum Sex {

public class User {
  private final int age;
  private final String name;
  private final Sex sex;

  public User(int age, String name, Sex sex) {
    this.age = age; = name; = sex;

  public int getAge() {
    return age;

  public boolean isMale() {
    return MALE.equals(sex);

Predicate<User> predicate = user -> user.getAge() >= 18;

A pretty useful functionality about predicates is that they can be composed together to form more complex ones. For instance, what do you do if suddenly you want a new User predicate that returns true for all users who are less than 18? Do you create a new predicate like the previous one but changing the >= by <? Luckily, you don’t have to because the Predicate interface provides 3 methods to compose several predicates: and, or and negate. So the previous example could be written as:

Predicate<User> older = user -> user.getAge() >= 18;
Predicate<User> younger = older.negate();

Similarly, if we want a predicate that returns true for all the male users older or equal to 18, we could write it as:

Predicate<User> older = user -> user.getAge() >= 18;
Predicate<User> adultMales = older.and(User::isMale);

That last example shows that we can use method references where a Predicate is expected. In fact, we can use a method reference wherever a functional interface is expected. We quickly saw method references in the previous post but we’ll discuss more about them later on.


The java.util.function.Function interface is defined as:

public interface Function<T, R> {
  R apply(T t);

What this basically does is take an input of type T and transform it somehow to return an object of type R. Note that the Predicate interface can be seen as a special case of a Function where R is always a boolean value. Following our User examples, imagine we want a function that given an User instance it returns that user’s name length. We could write this function like this:

Function<User,Integer> nameLength = user -> user.getName().length();

Like predicates, the Function interface also has some useful methods to compose several functions. The two methods offered are compose and andThen. The difference between them is subtle but important. To understand this better, imagine we have the following 2 functions:

Function<Integer,Integer> sumOne = number -> number + 1;
Function<Integer,Integer> duplicate = number -> number * 2;

We can then create 2 new functions in the following way:

Function<Integer, Integer> composed = sumOne.compose(duplicate);
Function<Integer, Integer> andThen = sumOne.andThen(duplicate);


The composed function will first apply duplicate and then apply sumOne on the result. In other words, composing sumOne with duplicate will result in sumOne(duplicate(x)) and the first System.out will print 5. The andThen function will do exactly the opposite, it will first apply sumOne and then apply duplicate on the result. In this case the second System.out will print 6.


The java.util.function.Consumer interface defines an accept method that takes a paramter of type T and returns no value. In other words:

public interface Consumer<T> {
    void accept(T t);

This interface is useful when you want to access an element and perform some operation on it. For instance, starting with Java 8, lists have a forEach method where you can pass a Consumer<T> and this function will be applied to each element on the list.

So imagine that you want to print to System.out each element on a list. You could do that in the following way:

List<String> users = Arrays.asList("java","8","rocks");
users.forEach(elem -> System.out.println(elem));

The implementation of the forEach method is actually quite straightforward:

void forEach(Consumer<? super T> action) {
  for (T t : this) {

Primitive functional interfaces

We saw a couple of generic, quite useful functional interfaces provided by the language: Predicate<T>, Function<T,R> and Consumer<T>. This is great for most cases where you want to use this interfaces for your own classes. But what happens when you need something like this for primitive types: int, double or boolean for instance?

In Java, each primitive type has a corresponding wrapper class. So int has an Integer class and boolean has a Boolean. Additionally, Java can handle conversions between these types for you automatically. This concept, known as autoboxing/unboxing is what allows you to write code like this:

List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 10; i++) {

This lets the developer write less code because he doesn’t need to worry about explicitly converting one type to the other. However, there is a performance impact involved. Is probably not a big deal if you do it occasionally here and there but when you are doing a boxing or unboxing on every iteration in a big list you will see a difference.

Going back to our functional interfaces, say you want to define a predicate that takes an int and returns a boolean telling us whether the number is odd or not. You can not define a Predicate<int> because int is not a class but you could do something like this:

Predicate<Integer> isOdd = i -> i % 2 == 1;

What happens when you call this predicate with an int is that this parameter gets autoboxed into an Integer. Again, this might not really be an issue if you are not using this Predicate in critical areas of your application or inside big loops.

If you don’t want your parameters boxed automatically for you and want to really use primitive types instead, Java 8 provides primitive specializations of its functional interfaces. In our example, we could use the IntPredicate interface, whose accept method only takes int parameters:

public interface IntPredicate {
  boolean test(int value);

Therefore, our previous example could be rewritten as:

IntPredicate isOdd = i -> i % 2 == 1;

Now, the parameter to the test method is treated as a primitive int all the way avoiding boxing and unboxing operations.

This primitive specializations extend to other types with similar names. So you are going to find DoublePredicate, IntFunction, LongConsumer and so on.

Method references

Lambda expressions are undoubtedly a great construct to make your code more compact. However, some times all you do in your lambda is to call an individual method potentially passing some parameter to it. In these cases you can often replace your lambda expression by a method reference.

Method references are compact ways to create lambda expressions for methods that already have a name. For instance, in the previous section we saw an example of the forEach method:

List<String> users = Arrays.asList("java","8","rocks");
users.forEach(elem -> System.out.println(elem));

Here, our lambda expression is only calling the System.out.println method. Therefore, we could rewrite it like this:

List<String> users = Arrays.asList("java","8","rocks");


Lambdas are one of the main additions to Java 8. And while you can still write code the way you used to do it before (using anonymous classes) chances are that you will start to see more and more lambdas going around other people code. So you should at least know they exist and how they can be used effectively.

Functional interfaces are not a small addition to the language but the fact that you can use a lambda expression or method reference every time you expect an interface is a huge deal. Is not only the fact that you remove a lot of boilerplate code but also that by doing that you are actually making your code easier to read and maintain. Having this concept applied to a lot of the existing language interfaces will also help a lot.

Take advantage of the interfaces defined for you in java.util.function. They are abstractions that come up quite frequently in practice and are very powerful given the way you can combine them. If you need to use them for primitive types like int or double remember that you have the option to use primitive specializations of these interfaces to avoid the performance cost of autoboxing.