smddzcy | yet another dev

Lambda Expressions in Java 8

☕️ 3 min read

Although I don’t prefer Java as my day-to-day language - mostly because of the sizes of my projects, it is a language that I really like programming in. A single Java code can run on almost all types of machines, the syntax is great, it is full OOP - something I really like, it has so many quality libraries to chose from and the list goes on. It has its bad parts, too. The major problem that affects my preference about Java, I think most of you out there will agree with me, is verbosity. But a big step was taken in Java 8 to a more functional & less verbose programming language. One of the most important features of Java 8 is lambda expressions.


Creating a Comparator before Java 8 was like this:

Comparator<Integer> beforeJava8 = new Comparator<Integer>() {
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;

With Java 8, it’s like this:

Comparator<Integer> withJava8 = (o1, o2) -> o1 - o2;

Right part of the expression above, (o1, o2) -> o1 - o2  is a lambda expression. It takes o1, o2  as parameters of the compare method of Comparator, and returns o1 - o2 . ->  is the separator - called lambda operator, of the lambda expression. It separates parameters from the body of the lambda expression. In a lambda expression, we don’t have to declare the types explicitly. Java can determine the types from the context. Since the type we declared as arguments of Comparator is Integer , Java can infer the types of o1 and o2 as Integer. But if we remove the type from the Comparator declaration and write something like this, code will not compile since Java can’t infer the types.

Comparator withJava8 = (o1, o2) -> o1 - o2; // fails to compile

By the way, lambdas are not just fancy anonymous classes. They separate themselves from anonymous classes in the lower levels of Java. Some of the differences between them are listed below.

  • Lambdas are not implemented using anonymous classes; meaning they don’t take space in the disk as .class  files and it speeds up the start time of JVM.
  • Types used in lambdas are not explicit, they are determined from the context.
  • Variables cannot be shadowed in a lambda expression. If attempted, it gives a compile time error.
  • this refers to the class that uses the lambda expression. On anonymous classes, it refers to anonymous class itself.

How it works

If we look at the declaration of Comparator interface, we see a new annotation.

public interface Comparator<T> {

Lambdas can only be used on functional interfaces, which has exactly one abstract method. (Default methods aren’t counted as abstract) @FunctionalInterface annotation is not something that functional interfaces must have, but it’s a good sign both for us and the Java to understand that an interface is a functional interface. Do we have to write all of the functional interfaces we’re going to use ? Of course not. Java 8 comes with a great number of functional interfaces, located in java.util.function package.


Consumer interface performs an operation on an argument.

Consumer<String> consumer = (s) -> System.out.println(s);


Supplier interface takes no argument, but produces a result.

Supplier<Long> supplier = () -> System.nanoTime();


Function interface takes one argument, and produces a result.

Function<String, Integer> function = (s) -> s.length();


Predicate interface takes one argument, and returns the result of an evaluation on that argument.

Predicate<String> predicate = (s) -> s.length() == 1;

Another great feature of Java 8 is Stream API, which will be covered on a later post. It provides a greater functionality to Java, and also it is a perfect fit for lambda expressions. Until then, Valéte!