What Are Lambda Expressions?
Lambda expressions add a new ability to the Java language: to pass a function as an argument to another method. Before lambda expressions you could do something similar using anonymous inner classes and functional interfaces, but the result code became less legible and clear:
1 2 3 4 5 6 7 8 9 10 11 12 |
// Anonymous inner class (h) and functional interface (Handler) Handler h = new Handler<Event>() { @Override public void handle(Event e) { print(e); } }); // Equivalent Lambda Expression. Simpler, isn’t it? Handler h = (Event e) -> print(e); |
Lambda expressions let you create single-method classes in a more compact way. The syntax is always the following:
1 2 3 |
parameter-list -> body |
parameter-list
is a comma-separated list of parameters. You don’t need to specify the type of the parameters (the compiler can determine it by looking at the functional interface method signature).body
can be an expression (it’s evaluated as any other expression and its value is returned) or a statement block (it’s evaluated like a method body).
The following are valid lambda expressions examples:
1 2 3 4 5 6 7 8 9 |
() -> 43 // empty parameter list i -> i + 1 // one parameter: no parentheses (int i) -> i + 1 // typed: use parentheses (int i, int j) -> i * j // more than one parameter (int i) -> System.out.println(i+1) // void method as expression i -> { System.out.println(i+1); } // block returning nothing i -> { return i+1; } // block returning a value |
Functional Interfaces
Now that you know how to write basic lambda expressions, the next question you may ask is:How do I create a method that takes a lambda expression as a parameter?
The answer is functional interfaces. A functional interface (known as a Single Abstract Method type before Java 8) is an interface with only one method. Any method that takes a functional interface as a parameter can be invoked with a lambda expression as an argument for that parameter.
The lambda expression must match the signature of the only functional interface method. If the method does not take any parameter then the parameter list of the lambda expression must be empty: ()
. If the method does not return anything then the lambda expression body must not return anything.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// given the following functional interface interface StringCleaner { String clean(String s); } // and the following method with a parameter of that type static void printCleanStrings(List<String> list, StringCleaner sc) { for (String s : list) System.out.println(sc.clean(s)); } // the following are valid invocations to the method using lambda expressions printCleanStrings(ls, s -> s.trim().toLowerCase()); printCleanStrings(ls, s -> { return s.trim().toLowerCase(); }); // invalid invocation: a block statement doesn't return anything // (and a String object is expected) printCleanStrings(ls, s -> { s.trim().toLowerCase();}); // invalid invocation: the block returns nothing (void) // (and a String object is expected) printCleanStrings(ls, s -> { s.trim().toLowerCase(); return; }); |
Predicates
You can create your own functional interfaces to use with lambda expressions, but Java already provide some that cover most use cases and therefore eliminates the need for every programmer to write the same things again and again.Predicate
is one of these interfaces provided in the package java.util.function
. There are some other useful interfaces in that package (e.g. Function
and Consumer
) but you don’t need to know about them for the exam.
1 2 3 4 5 |
interface Predicate<T> { boolean test(T t); } |
Predicates are useful whenever you want to invoke a method passing a lambda expression that returns a boolean value:
1 2 3 4 5 6 7 8 9 10 11 |
// this method prints all Devices compliant with the Predicate static void printReport(List<Device> ld, Predicate<Device> p) { for (Device d : ld) if (p.test(d)) System.out.println(d); } // using a lambda expression to print only mobile devices printReport(list, d -> d.getType() == Device.Mobile); |
In the previous example we use the lambda expression to easily filter the list of Device
objects that the printReport
method prints out to the standard output. Notice that since Predicate
is a generic interface, when declaring it you must indicate the actual type of the interface (Predicate<Device>
in this case).