Lambda Expression Implementing Functional Interfaces with Lambda Expression Functional Interface Interface that has only one abstract method. These do not count: Default methods Static methods 🟢 Object methods In ⚪ Collection<E> you may find 🟢 Object methods which are changing the documentation of those methods. However, ⚪ Collection<E> itself is not a Functional Interface as it has many abstract methods. Lambda Expression Implementation of a Functional Interface. Lambda Expression is not another way of writing instances of anonymous classes. Anonymous class may implement any kind of Interface, not necessarily Functional, and it can extend any kind of class, whether it is a concrete class or an abstract class. Sometimes people say that a Lambda Expression is an anonymous method. Java is object-oriented programming language and thanks to introduction of Lambda Expression some patterns from functional programming have been added to it. Functional Interface may be annotated with the special annotation called @FunctionalInterface , but it’s not mandatory. This is for backward compatibility (e.g. since @FunctionalInterface is optional, ⚪ Runnable can be a Functional Interface). How to implement a Functional Interface in 3 easy steps: copy and paste a block of parameters of an abstract method: () draw an arrow operator: → provide an implementation which is going to fit your needs Creating Lambdas with the JDK The java.util.function package is organized in 4 categories: ⚪ Suppliers<T> - do not take any argument and return an object of T type. Supplier is used in the generate operation of the Stream API public interface Supplier<T> { T get(); } // ... Supplier<String> supplier = () -> "Hello!"; ⚪ Consumers<T> - consume an object of T type and do not return anything. Consumer is used in the forEach operation of the Stream API public interface Consumer<T> { void accept(T t); } // ... Consumer<String> consumer = s -> System.out.println(s); ⚪ Predicates<T> - take an object of T type, test it and return boolean. Predicate is used in the filter operation of the Stream API public interface Predicate<T> { boolean test(T t); } // ... Predicate<String> predicate = s -> s.isEmpty(); ⚪ Functions<T, R> - take an object of T type and return an object of R type. Function is used in the map operation of the Stream API public interface Function<T, R> { R apply(T t); } // ... Function<LocalDate, Month> function = date -> date.getMonth(); Functional Interfaces which are not part of java.util.function package: ⚪ Runnable - does not take any argument and does not return anything. Defined to model task in Java concurrent programming public interface Runnable { void run(); } // ... Runnable alert = () -> System.out.println("I am Groot"); ⚪ Comparator<T> - takes two objects of T type, compares them and returns int. A comparison function, which imposes a total ordering on some collection of objects public interface Comparator<T> { int compare(T o1, T o2); } // ... Comparator<String> comparator = (s1, s2) -> s1.compareTo(s2); Creating Lambdas with specialized Interfaces Lambda Expressions are compiled using specific bytecode instruction called invokedynamic introduced in Java 7. Performance is 60x better in Lambda Expressions than Anonymous Classes. It can be even faster. Just avoid autoboxing and unboxing when writing Lambda Expressions. If you want to improve the performance you need to use specialized Interfaces for primitive types: Bad Practice: autoboxing and unboxing BiFunction<Integer, Integer, Integer> function1 = (i1, i2) -> Math.max(i1, i2); int max1 = function1.apply(10, 20); Best Practice: use specialized Interfaces for primitive types IntBinaryOperator function2 = (i1, i2) -> Math.max(i1, i2); int max2 = function2.applyAsInt(10, 20); Another example would be the creation of ⚪ Comparator<T> for the following list: List<String> strings = Arrays.asList("one", "two", "three", "four", "five"); Bad Practice: autoboxing and unboxing Comparator<String> cmp1 = (s1, s2) -> Integer.compare(s1.length(), s2.length()); strings.sort(cmp1); Best Practice: use specialized Interfaces for primitive types ToIntFunction<String> toLength = s -> s.length(); Comparator<String> cmp2 = Comparator.comparingInt(toLength); strings.sort(cmp2); Creating Lambdas by chaining Lambda Expressions can be chained by using default methods of the Interface. Creating a ⚪ Consumer<T> by chaining two ⚪ Consumers<T> Consumer<String> c1 = s -> System.out.println("c1 consumes " + s); Consumer<String> c2 = s -> System.out.println("c2 consumes " + s); Consumer<String> c3 = c1.andThen(c2); c3.accept("Hello"); // Prints c1 consumes Hello c2 consumes Hello Creating a ⚪ Predicate<T> by chaining two ⚪ Predicates<T> Predicate<String> isNull = s -> s == null; Predicate<String> isEmpty = s -> s.isEmpty(); Predicate<String> notNullAndNotEmpty = isNull.negate().and(isEmpty.negate()); System.out.println("For null = " + notNullAndNotEmpty.test(null)); System.out.println("For empty = " + notNullAndNotEmpty.test("")); System.out.println("For Hello = " + notNullAndNotEmpty.test("Hello")); // Prints For null = false For empty = false For Hello = true Creating a ⚪ Comparator<T> by chaining two ⚪ Comparators<T> (sort by name descending, then by age descending): User sarah = new User("Sarah", 28); User james = new User("James", 35); User mary = new User("Mary", 33); User john1 = new User("John", 24); User john2 = new User("John", 25); List<User> users = Arrays.asList(sarah, james, mary, john1, john2); Comparator<User> cmpName = Comparator.comparing(user -> user.getName()); Comparator<User> cmpAge = Comparator.comparingInt(user -> user.getAge()); Comparator<User> comparator = cmpName.thenComparing(cmpAge); Comparator<User> reversed = comparator.reversed(); users.sort(reversed); users.forEach(user -> System.out.println(user)); // Prints User{name='Sarah', age=28} User{name='Mary', age=33} User{name='John', age=25} User{name='John', age=24} User{name='James', age=35}