Lambda Expressions Java SE 8–Java SE 9 (Early Access)
This draft deletes the entire topic.
Introduction
Examples
-
Functional Interfaces
Lambdas can only operate on a functional interface, which is an interface with just one abstract method. Functional interfaces can have any number of
default
orstatic
methods. (For this reason, they are sometimes refered to as Single Abstract Method Interfaces, or SAM Interfaces).interface Foo1 { void bar(); } interface Foo2 { int bar(boolean baz); } interface Foo3 { String bar(Object baz, int mink); } interface Foo4 { default String bar() { // default so not counted return "baz"; } void quux(); }
When declaring a functional interface the
@FunctionalInterface
annotation can be added. This has no special effect, but a compiler error will be generated if this annotation is applied to an interface which is not functional, thus acting as a reminder that the interface should not be changed.@FunctionalInterface interface Foo5 { void bar(); } @FunctionalInterface interface BlankFoo1 extends Foo3 { // inherits abstract method from Foo3 } @FunctionalInterface interface Foo6 { void bar(); boolean equals(Object obj); // overrides one of Object's method so not counted }
Conversely, this is not a functional interface, as it has more than one abstract method:
interface BadFoo { void bar(); void quux(); // <-- Second method prevents lambda: which one should be considered as lambda? }
This is also not a functional interface, as it does not have any methods:
interface BlankFoo2 { }
Java 8 also provides a number of generic templated functional interfaces in the package
java.util.function
. For example, the built-in interfacePredicate<T>
wraps a single method which inputs a value of typeT
and returns aboolean
.
Lambda Expressions
The basic structure of a Lambda expression is:
fi
will then hold a singleton instance of an anonymous class which implementsFunctionalInterface
and where the one method's definition is{ System.out.println("Hello"); }
. In other words, the above is mostly equivalent to:FunctionalInterface fi = new FunctionalInterface() { @Override public void theOneMethod() { System.out.println("Hello"); } };
You cannot specify the name of the method when using a lambda—but you shouldn't need to, because a functional interface must have only one abstract method, so Java overrides that one.
In cases where the type of the lambda is not certain, (e.g. overloaded methods) you can add a cast to the lambda to tell the compiler what its type should be, like so:
Object fooHolder = (Foo1) () -> System.out.println("Hello"); System.out.println(fooHolder instanceof Foo1); // returns true
If the functional interface's single method takes parameters, the local formal names of these should appear between the brackets of the lambda. There is no need to declare the type of the parameter or return as these are taken from the interface (although it is not an error to declare the parameter types if you want to). Thus, these two examples are equivalent:
Foo2 longFoo = new Foo2() { @Override public int bar(boolean baz) { return baz ? 1 : 0; } }; Foo2 shortFoo = (x) -> { return x ? 1 : 0; };
The parentheses around the argument can be omitted if the function only has one argument:
Foo2 np = x -> { return x ? 1 : 0; }; // okay Foo3 np2 = x, y -> x.toString() + y // not okay
Implicit Returns
If the code placed inside a lambda is a Java expression rather than a statement, it is treated as a method which returns the value of the expression. Thus, the following two are equivalent:
IntUnaryOperator addOneShort = (x) -> (x + 1); IntUnaryOperator addOneLong = (x) -> { return (x + 1); };
Accessing Local Variables (value closures)
Since lambdas are syntactic shorthand for anonymous classes, they follow the same rules for accessing local variables in the enclosing scope; the variables must be treated as
final
and not modified inside the lambda.IntUnaryOperator makeAdder(int amount) { return (x) -> (x + amount); // Legal even though amount will go out of scope // because amount is not modified } IntUnaryOperator makeAccumulator(int value) { return (x) -> { value += x; return value; }; // Will not compile }
If it is necessary to wrap a changing variable in this way, a regular object that keeps a copy of the variable should be used. Read more in Java Closures with lambda expressions.
Accepting Lambdas
Because a lambda is an implementation of an interface, nothing special needs to be done to make a method accept a lambda: any function which takes a functional interface can also accept a lambda.
public void passMeALambda(Foo1 f) { f.bar(); } passMeALambda(() -> System.out.println("Lambda called"));
-
Prior to Java 8, it was necessary to implement the
java.util.Comparator
interface with an anonymous (or named) class when sorting a collection:Java SE 1.2Collections.sort( personList, new Comparator<Person>() { public int compare(Person p1, Person p2){ return p1.getFirstName().compareTo(p2.getFirstName()); } } );
Starting with Java 8, the anonymous class can be replaced with a lambda expression. Note that the types for the parameters
p1
andp2
can be left out, as the compiler will infer them automatically:Collections.sort( personList, (p1, p2) -> p1.getFirstName().compareTo(p2.getFirstName()) );
The example can be simplified by using
Comparator.comparing
and method references expressed using the::
(double colon) symbol.Collections.sort( personList, Comparator.comparing(Person::getFirstName) );
A static import allows us to express this more concisely, but it is debatable whether this improves overall readability:
import static java.util.Collections.sort; import static java.util.Comparator.comparing; //... sort(personList, comparing(Person::getFirstName));
Comparators built this way can also be chained together. For example, after comparing people by their first name, if there are people with the same first name, the
thenComparing
method with also compare by last name:sort(personList, comparing(Person::getFirstName).thenComparing(Person::getLastName));
-
-
Method references allow predefined static or instance methods that adhere to a compatible functional interface to be passed as arguments instead of an anonymous lambda expression.
Assume that we have a model:
class Person { private final String name; private final String surname; public Person(String name, String surname){ this.name = name; this.surname = surname; } public String getName(){ return name; } public String getSurname(){ return surname; } } List<Person> people = getSomePeople();
Instance method reference (to an arbitrary instance)
people.stream().map(Person::getName)
The equivalent lambda:
people.stream().map(person -> person.getName())
In this example, a method reference to the instance method
getName()
of typePerson
, is being passed. Since it's known to be of the collection type, the method on the instance (known later) will be invoked.
Instance method reference (to a specific instance)
people.forEach(System.out::println);
Since
System.out
is an instance ofPrintStream
, a method reference to this specific instance is being passed as an argument.The equivalent lambda:
people.forEach(person -> System.out.println(person));
Static method reference
Also for transforming streams we can apply references to static methods:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); numbers.stream().map(String::valueOf)
This example passes a reference to the static
valueOf()
method on theString
type. Therefore, the instance object in the collection is passed as an argument tovalueOf()
.The equivalent lambda:
numbers.stream().map(num -> String.valueOf(num))
Reference to a constructor
List<String> strings = Arrays.asList("1", "2", "3"); strings.stream().map(Integer::new)
Read Collect Elements of a Stream into a Collection to see how to collect elements to collection.
The single String argument constructor of the
Integer
type is being used here, to construct an integer given the string provided as the argument. In this case, as long as the string represents a number, the stream will be mapped to Integers. The equivalent lambda:strings.stream().map(s -> new Integer(s));
Cheat-Sheet
Method Reference Format Code Equivalent Lambda Static method TypeName::method
(args) -> TypeName.method(args)
Non-static method (from instance) instance::method
(args) -> instance.method(args)
Non-static method (no instance) TypeName::method
(instance, args) -> instance.method(args)
Constructor TypeName::new
(args) -> new TypeName(args)
-
Sometimes you may want to have a lambda expression implementing more than one interface. This is mostly useful with marker interfaces (such as java.io.Serializable) since they don't add abstract methods.
For example, you want to create a
TreeSet
with a customComparator
and then serialize it and send it over the network. The trivial approach:TreeSet<Long> ts = new TreeSet<>((x, y) -> Long.compare(y, x));
doesn't work since the lambda for the comparator does not implement
Serializable
. You can fix this by using intersection types and explicitly specifying that this lambda needs to be serializable:TreeSet<Long> ts = new TreeSet<>( (Comparator<Long> & Serializable) (x, y) -> Long.compare(y, x));
If you're frequently using intersection types (for example, if you're using a framework such as Apache Spark where almost everything has to be serializable), you can create empty interfaces and use them in your code instead:
public interface SerializableComparator extends Comparator<Long>, Serializable {} public class CustomTreeSet { public CustomTreeSet(SerializableComparator comparator) {} }
This way you're guaranteed that the passed comparator will be serializable.
-
A lambda closure is created when a lambda expression references the variables of an enclosing scope (global or local). The rules for doing this are the same as those for inline methods and anonymous classes.
Local variables from an enclosing scope that are used within a lambda have to be
final
. With Java 8 (the earliest version that supports lambdas), they don't need to be declaredfinal
in the outside context, but must be treated that way. For example:int n = 0; // With Java 8 there is no need to explicit final Runnable r = () -> { // Using lambda int i = n; // do something };
This is legal as long as the value of the
n
variable is not changed. If you try to change the variable, inside or outside the lambda, you will get the following compilation error:"local variables referenced from a lambda expression must be final or effectively final".
For example:
int n = 0; Runnable r = () -> { // Using lambda int i = n; // do something }; n++; // Will generate an error.
If it is necessary to use a changing variable within a lambda, the normal approach is to declare a
final
copy of the variable and use the copy. For exampleint n = 0; final int k = n; // With Java 8 there is no need to explicit final Runnable r = () -> { // Using lambda int i = k; // do something }; n++; // Now will not generate an error r.run(); // Will run with i = 0 because k was 0 when the lambda was created
Naturally, the body of the lambda does not see the changes to the original variable.
Note that Java does not support true closures. A Java lambda cannot be created in a way that allows it to see changes in the environment in which it was instantiated. If you to implement a closure that observes or makes changes to its environment, you should simulate it using a regular class. For example:
// Does not compile ... public IntUnaryOperator createAccumulator() { int value = 0; IntUnaryOperator accumulate = (x) -> { value += x; return value; }; return accumulate; }
The above will not compile for reasons discussed previously. We can work around the compilation error as follows:
// Compiles, but is incorrect ... public class AccumulatorGenerator { private int value = 0; public IntUnaryOperator createAccumulator() { IntUnaryOperator accumulate = (x) -> { value += x; return value; }; return accumulate; } }
The problem is that the breaks the design contract for the
IntUnaryOperator
interface which states that instances should be functional and stateless. If such a closure is passed to built-in functions that accept functional objects, it is liable to cause crashes or erroneous behavior. Closures that encapsulate mutable state should be implemented as regular classes. For example.// Correct ... public class Accumulator { private int value = 0; public int accumulate(int x) { value += x; return value; } }
-
Anonymous class listener
JButton btn = new JButton("My Button"); btn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Button was pressed"); } });
Lambda listener
JButton btn = new JButton("My Button"); btn.addActionListener(e -> { System.out.println("Button was pressed"); });
-
Lambdas are meant to provide inline implementation code for single method interfaces and the ability to pass them around as we have been doing with normal variables. We call them Functional Interface.
For example, writing a Runnable in anonymous class and starting a Thread looks like:
//Old way new Thread( new Runnable(){ public void run(){ System.out.println("run logic..."); } } ).start(); //lambdas, from Java 8 new Thread( ()-> System.out.println("run logic...") ).start();
Now, in line with above, lets say you have some custom interface:
interface TwoArgInterface { int operate(int a, int b); }
How do you use lambda to give implementation of this interface in your code? Same as Runnable example shown above. See the driver program below:
public class CustomLambda { public static void main(String[] args) { TwoArgInterface plusOperation = (a, b) -> a + b; TwoArgInterface divideOperation = (a,b)->{ if (b==0) throw new IllegalArgumentException("Divisor can not be 0"); return a/b; }; System.out.println("Plus operation of 3 and 5 is: " + plusOperation.operate(3, 5)); System.out.println("Divide operation 50 by 25 is: " + divideOperation.operate(50, 25)); } }
-
The
return
method only returns from the lambda, not the outer method.Beware that this is different from Scala and Kotlin!
void threeTimes(IntConsumer r) { for (int i = 0; i < 3; i++) { r.accept(i); } } void demo() { threeTimes(i -> { System.out.println(i); return; // Return from lambda to threeTimes only! }); }
This can lead to unexpected behavior when attempting to write own language constructs, as in builtin constructs such as
for
loopsreturn
behaves differently:void demo2() { for (int i = 0; i < 3; i++) { System.out.println(i); return; // Return from 'demo2' entirely } }
In Scala and Kotlin,
demo
anddemo2
would both only print0
. But this is not more consistent. The Java approach is consistent with refactoring and the use of classes - thereturn
in the code at the top, and the code below behaves the same:void demo3() { threeTimes(new MyIntConsumer()); } class MyIntConsumer implements IntConsumer { public void accept(int i) { System.out.println(i); return; } }
Therefore, the Java
return
is more consistent with class methods and refactoring, but less with thefor
andwhile
builtins, these remain special.Because of this, the following two are equivalent in Java:
IntStream.range(1, 4) .map(x -> x * x) .forEach(System.out::println); IntStream.range(1, 4) .map(x -> { return x * x; }) .forEach(System.out::println);
Furthermore, the use of try-with-resources is safe in Java:
class Resource implements AutoCloseable { public void close() { System.out.println("close()"); } } void executeAround(Consumer<Resource> f) { try (Resource r = new Resource()) { System.out.print("before "); f.accept(r); System.out.print("after "); } } void demo4() { executeAround(r -> { System.out.print("accept() "); return; // Does not return from demo4, but frees the resource. }); }
will print
before accept() after close()
. In the Scala and Kotlin semantics, the try-with-resources would not be closed, but it would printbefore accept()
only. -
There are several good examples of using lambdas as a FunctionalInterface in simple scenarios. A fairly common use case that can be improved by lambdas is what is called the Execute-Around pattern. In this pattern, you have a set of standard setup/teardown code that is needed for multiple scenarios surrounding use case specific code. A few common example of this are file io, database io, try/catch blocks.
interface DataProcessor { void process( Connection connection ) throws SQLException;; } public void doProcessing( DataProcessor processor ) throws SQLException{ try (Connection connection = DBUtil.getDatabaseConnection();) { processor.process(connection); connection.commit(); } }
Then to call this method with a lambda it might look like:
public static void updateMyDAO(MyVO vo) throws DatabaseException { doProcessing((Connection conn) -> MyDAO.update(conn, ObjectMapper.map(vo))); }
This is not limited to I/O operations. It can apply to any scenario where similar setup/tear down tasks are applicable with minor variations. The main benefit of this Pattern is code re-use and enforcing DRY (Don't Repeat Yourself).
-
Since Java lambdas are closures, they can "capture" the values of variables in the enclosing lexical scope. While not all lambdas capture anything -- simple lambdas like
s -> s.length()
capture nothing and are called stateless -- capturing lambdas require a temporary object to hold the captured variables. In this code snippet, the lambda() -> j
is a capturing lambda, and may cause an object to be allocated when it is evaluated:public static void main(String[] args) throws Exception { for (int i = 0; i < 1000000000; i++) { int j = i; doSomethingWithLambda(() -> j); } }
Although it might not be immediately obvious since the
new
keyword doesn't appear anywhere in the snippet, this code creates 1000000000 separate instances of the() -> j
lambda. -
Traditional way
interface MathOperation{ boolean unaryOperation(int num); } public class LambdaTry { public static void main(String[] args) { MathOperation isEven = new MathOperation() { @Override public boolean unaryOperation(int num) { return num%2 == 0; } }; System.out.println(isEven.unaryOperation(25)); System.out.println(isEven.unaryOperation(20)); } }
Lambda style
- Remove class name and functional interface body.
public class LambdaTry { public static void main(String[] args) { MathOperation isEven = (int num) -> { return num%2 == 0; }; System.out.println(isEven.unaryOperation(25)); System.out.println(isEven.unaryOperation(20)); } }
- Optional type declaration
MathOperation isEven = (num) -> { return num%2 == 0; };
- Optional parenthesis around parameter, if it is single parameter
MathOperation isEven = num -> { return num%2 == 0; };
- Optional curly braces, if there is only one line in function body
- Optional return keyword, if there is only one line in function body
MathOperation isEven = num -> num%2 == 0;
-
Starting with Java 8, you can use lambda expressions & predicates.
Example: Use a lambda expressions & a predicate to get a certain value from a list. In this example every person will be printed out with the fact if they are 18 and older or not.
Person Class:
public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public String getName() { return name; } }
The built-in interface Predicate from the java.util.function.Predicate packages is a functional interface with a
boolean test(T t)
method.Example Usage:
import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; public class LambdaExample { public static void main(String[] args) { List<Person> personList = new ArrayList<Person>(); personList.add(new Person("Jeroen", 20)); personList.add(new Person("Jack", 5)); personList.add(new Person("Lisa", 19)); print(personList, p -> p.getAge() >= 18); } private static void print(List<Person> personList, Predicate<Person> checker) { for (Person person : personList) { if (checker.test(person)) { System.out.print(person + " matches your expression."); } else { System.out.println(person + " doesn't match your expression."); } } } }
The
print(personList, p -> p.getAge() >= 18);
method takes a lambda expression (because the Predicate is used a parameter) where you can define the expression that is needed. The checker's test method checks if this expression is correct or not:checker.test(person)
.You can easily change this to something else, for example to
print(personList, p -> p.getName().startsWith("J"));
. This will check if the person's name starts with a "J".
Syntax
- () -> { return expression; } // Zero-arity with function body to return a value.
- () -> expression // Shorthand for the above declaration; there is no semicolon for expressions.
- () -> { function-body } // Side-effect in the lambda expression to perform operations.
- parameterName -> expression // One-arity lambda expression. In lambda expressions with only one argument, the parenthesis can be removed.
- (Type parameterName, Type secondParameterName, ...) -> expression // lambda evaluating an expression with parameters listed to the left
- (parameterName, secondParameterName, ...) -> expression // Shorthand that removes the parameter types for the parameter names. Can only be used in contexts that can be inferred by the compiler where the given parameter list size matches one (and only one) of the size of the functional interfaces expected.
Topic Outline
Introduction
- Introduction to Java lambdas
- Using Lambda Expressions to Sort a Collection
- Method References
- Implementing multiple interfaces
- Java Closures with lambda expressions.
- Lambda - Listener Example
- Using lambda expression with your own functional interface
- `return` only returns from the lambda, not the outer method
- Lambdas and Execute-around Pattern
- Lambdas and memory utilization
- Traditional style to Lambda style
- Using lambda expressions & predicates to get a certain value(s) from a list
Syntax
Sign up or log in
Save edit as a guest
Join Stack Overflow
Using Google
Using Facebook
Using Email and Password
We recognize you from another Stack Exchange Network site!
Join and Save Draft