The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available.
SeeDev.java for updated tutorials taking advantage of the latest releases.
SeeJava Language Changes for a summary of updated language features in Java SE 9 and subsequent releases.
SeeJDK Release Notes for information about new features, enhancements, and removed or deprecated options for all JDK releases.
One issue with anonymous classes is that if the implementation of your anonymous class is very simple, such as an interface that contains only one method, then the syntax of anonymous classes may seem unwieldy and unclear. In these cases, you're usually trying to pass functionality as an argument to another method, such as what action should be taken when someone clicks a button. Lambda expressions enable you to do this, to treat functionality as method argument, or code as data.
The previous section,Anonymous Classes, shows you how to implement a base class without giving it a name.Although this is often more concise than a named class, for classeswith only one method, even an anonymous class seems a bitexcessive and cumbersome. Lambda expressions let you express instances ofsingle-method classes more compactly.
This section covers the following topics:
Suppose that you are creating a social networking application. Youwant to create a feature that enables an administrator to performany kind of action, such as sending a message, on members of thesocial networking application that satisfy certain criteria. The following table describes this use case in detail:
Field | Description |
---|---|
Name | Perform action on selected members |
Primary Actor | Administrator |
Preconditions | Administrator is logged in to the system. |
Postconditions | Action is performed only on members that fit the specified criteria. |
Main Success Scenario |
|
Extensions | 1a. Administrator has an option to preview those members who match the specified criteria before he or she specifies the action to be performed or before selecting theSubmit button. |
Frequency of Occurrence | Many times during the day. |
Suppose that members of this social networking application arerepresented by the followingPerson
class:
public class Person { public enum Sex { MALE, FEMALE } String name; LocalDate birthday; Sex gender; String emailAddress; public int getAge() { // ... } public void printPerson() { // ... }}
Suppose that the members of your social networking applicationare stored in aList<Person>
instance.
This section begins with a naive approach to this use case. It improves upon this approach with local and anonymous classes, and then finishes with an efficient and concise approach using lambda expressions. Find the code excerpts described in this section in the exampleRosterTest
.
One simplistic approach is to create several methods; each method searches for members that match one characteristic, such as gender or age. The following method prints members that are older than a specifiedage:
public static void printPersonsOlderThan(List<Person> roster, int age) { for (Person p : roster) { if (p.getAge() >= age) { p.printPerson(); } }}
Note: AList
is an orderedCollection
. Acollection is an objectthat groups multiple elements into a single unit. Collections areused to store, retrieve, manipulate, and communicate aggregatedata. For more information about collections, see theCollections trail.
This approach can potentially make your applicationbrittle, which is the likelihood of an application not working because of the introduction of updates (such as newer data types). Suppose that you upgrade your application and change the structure of thePerson
class such that it contains different member variables; perhaps the class records and measures ages with a different data type or algorithm. You would have to rewrite a lot of your API to accommodate this change. In addition, this approach is unnecessarily restrictive; what if youwanted to print members younger than a certain age, for example?
The following method is more generic thanprintPersonsOlderThan
; it prints members within a specified range of ages:
public static void printPersonsWithinAgeRange( List<Person> roster, int low, int high) { for (Person p : roster) { if (low <= p.getAge() && p.getAge() < high) { p.printPerson(); } }}
What if you want to print members of a specified sex, or a combination of a specified gender and age range? What if you decide to change thePerson
class and add other attributes such as relationship status or geographical location? Although this method is more generic thanprintPersonsOlderThan
, trying to create a separate method for each possible search query can still lead to brittle code. You can instead separate the code that specifies the criteria for which you want to search in a different class.
The following method prints members that match search criteria that you specify:
public static void printPersons( List<Person> roster, CheckPerson tester) { for (Person p : roster) { if (tester.test(p)) { p.printPerson(); } }}
This method checks eachPerson
instance contained in theList
parameterroster
whether it satisfies the search criteria specified in theCheckPerson
parametertester
by invoking the methodtester.test
. If the methodtester.test
returns atrue
value, then the methodprintPersons
is invoked on thePerson
instance.
To specify the search criteria, you implement theCheckPerson
interface:
interface CheckPerson { boolean test(Person p);}
The following class implements theCheckPerson
interface by specifying an implementation for the methodtest
. This method filters members that are eligible for Selective Service in the United States: it returns atrue
value if itsPerson
parameter is male and between the ages of 18 and 25:
class CheckPersonEligibleForSelectiveService implements CheckPerson { public boolean test(Person p) { return p.gender == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; }}
To use this class, you create a newinstance of it and invoke theprintPersons
method:
printPersons( roster, new CheckPersonEligibleForSelectiveService());
Although this approach is less brittle—you don't have to rewrite methods if you change the structure of thePerson
—you still have additional code: a new interface and a local class for each search you plan to perform in your application. BecauseCheckPersonEligibleForSelectiveService
implements an interface, you can use an anonymous classinstead of a local class and bypass the need to declare a new class for each search.
One of the arguments of the following invocation of the methodprintPersons
is an anonymous class that filters members that are eligible for Selective Service in the United States: those who are male and between the ages of 18 and 25:
printPersons( roster, new CheckPerson() { public boolean test(Person p) { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; } });
This approach reduces the amount of code required because you don't have to create a new class for each search that you want to perform. However, the syntax of anonymous classes is bulky considering that theCheckPerson
interface contains only one method. In thiscase, you can use a lambda expression instead of an anonymous class, asdescribed in the next section.
TheCheckPerson
interface is afunctional interface. A functionalinterface is any interface that contains only oneabstract method. (A functional interface may contain one or moredefault methods orstatic methods.) Becausea functional interface contains only one abstract method, you canomit the name of that methodwhen you implement it. To do this, instead of using an anonymousclass expression, you use alambdaexpression, which ishighlighted in the following method invocation:
printPersons( roster,(Person p) -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25);
SeeSyntax of Lambda Expressions for information about how to define lambda expressions.
You can use a standard functional interface in place of the interfaceCheckPerson
, which reduces even further the amount of code required.
Reconsider theCheckPerson
interface:
interface CheckPerson { boolean test(Person p);}
This is a verysimple interface. It's a functional interface because it containsonly one abstract method. This method takes one parameter and returns aboolean
value. The method is so simple that it might not be worthit to define one in your application. Consequently, the JDKdefines several standard functional interfaces, which you canfind in the packagejava.util.function
.
For example, you can use thePredicate<T>
interface in place ofCheckPerson
. Thisinterface contains the methodbooleantest(T t)
:
interface Predicate<T> { boolean test(T t);}
The interfacePredicate<T>
is an example of a generic interface. (For more information about generics, see theGenerics (Updated) lesson.) Generic types (such as generic interfaces) specify one or more type parameters within angle brackets (<>
). This interface contains only one type parameter,T
. When you declare or instantiate a generic type with actual type arguments, you have a parameterized type. For example, the parameterized typePredicate<Person>
is the following:
interface Predicate<Person
> { boolean test(Person
t);}
This parameterized type contains a method that has the same return type and parameters asCheckPerson.boolean test(Person p)
. Consequently, you can usePredicate<T>
in place ofCheckPerson
as the following method demonstrates:
public static void printPersonsWithPredicate( List<Person> roster, Predicate<Person> tester) { for (Person p : roster) { if (tester.test(p)) { p.printPerson(); } }}
As a result,the following method invocation is the same as when you invokedprintPersons
inApproach 3: Specify Search Criteria Code in a Local Class to obtain members who are eligible for Selective Service:
printPersonsWithPredicate( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25);
This is not the only possible place in this method to use a lambda expression. The following approach suggests other ways to use lambda expressions.
Reconsider the methodprintPersonsWithPredicate
to see where else you could use lambda expressions:
public static void printPersonsWithPredicate( List<Person> roster, Predicate<Person> tester) { for (Person p : roster) { if (tester.test(p)) { p.printPerson(); } }}
This method checks eachPerson
instance contained in theList
parameterroster
whether it satisfies the criteria specified in thePredicate
parametertester
. If thePerson
instance does satisfy the criteria specified bytester
, the methodprintPerson
is invoked on thePerson
instance.
Instead of invoking the methodprintPerson
, you can specify a different action to perform on thosePerson
instances that satisfy the criteria specified bytester
. You can specify this action with a lambda expression. Suppose you want a lambda expression similar toprintPerson
, one that takes one argument (an object of typePerson
) and returns void. Remember, to use a lambda expression, you need to implement a functional interface. In this case, you need a functional interface that contains an abstract method that can take one argument of typePerson
and returns void. TheConsumer<T>
interface contains the methodvoid accept(T t)
, which has these characteristics. Thefollowing method replaces the invocationp.printPerson()
with aninstance ofConsumer<Person>
that invokes the methodaccept
:
public static void processPersons( List<Person> roster, Predicate<Person> tester,Consumer<Person> block) { for (Person p : roster) { if (tester.test(p)) {block.accept(p); } }}
As aresult, the following method invocation is the same as when you invokedprintPersons
inApproach 3: Specify Search Criteria Code in a Local Class to obtain members who are eligible for Selective Service. The lambda expression used toprint members is highlighted:
processPersons( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25,p -> p.printPerson());
What if you want to do more with your members' profiles than printing them out. Suppose that you want to validate the members' profiles or retrievetheir contact information? In this case, you need a functionalinterface that contains an abstract method that returns a value.TheFunction<T,R>
interface contains themethodR apply(Tt)
. The following method retrieves the dataspecified by the parametermapper
, andthen performs an action on it specified by theparameterblock
:
public static void processPersonsWithFunction( List<Person> roster, Predicate<Person> tester, Function<Person, String> mapper, Consumer<String> block) { for (Person p : roster) { if (tester.test(p)) { String data = mapper.apply(p); block.accept(data); } }}
The following method retrieves the email address from each membercontained inroster
who is eligible for Selective Service andthen prints it:
processPersonsWithFunction( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25, p -> p.getEmailAddress(), email -> System.out.println(email));
Reconsider the methodprocessPersonsWithFunction
. The following is a generic version of it that accepts, as a parameter, a collection that contains elements of any data type:
public static <X, Y> void processElements( Iterable<X> source, Predicate<X> tester, Function <X, Y> mapper, Consumer<Y> block) { for (X p : source) { if (tester.test(p)) { Y data = mapper.apply(p); block.accept(data); } }}
To print the e-mail address of members who are eligible for Selective Service, invoke theprocessElements
method as follows:
processElements( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25, p -> p.getEmailAddress(), email -> System.out.println(email));
This method invocation performs the following actions:
source
. In this example, it obtains a source ofPerson
objects from the collectionroster
. Notice that the collectionroster
, which is a collection of typeList
, is also an object of typeIterable
.Predicate
objecttester
. In this example, thePredicate
object is a lambda expression that specifies which members would be eligible for Selective Service.Function
objectmapper
. In this example, theFunction
object is a lambda expression that returns the e-mail address of a member.Consumer
objectblock
. In this example, theConsumer
object is a lambda expression that prints a string, which is the e-mail address returned by theFunction
object.You can replace each of these actions with an aggregate operation.
The following example uses aggregate operations to print the e-mail addresses of those members contained in the collectionroster
who are eligible for Selective Service:
roster .stream() .filter( p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25) .map(p -> p.getEmailAddress()) .forEach(email -> System.out.println(email));
The following table maps each of the operations the methodprocessElements
performs with the corresponding aggregate operation:
processElements Action | Aggregate Operation |
---|---|
Obtain a source of objects | Stream<E>stream() |
Filter objects that match aPredicate object | Stream<T>filter(Predicate<? super T> predicate) |
Map objects to another value as specified by aFunction object | <R> Stream<R>map(Function<? super T,? extends R> mapper) |
Perform an action as specified by aConsumer object | voidforEach(Consumer<? super T> action) |
The operationsfilter
,map
, andforEach
areaggregate operations. Aggregate operations process elements from a stream, not directly from a collection (which is the reason why the first method invoked in this example isstream
). Astream is a sequence of elements. Unlike a collection, it is not a data structure that stores elements. Instead, a stream carries values from a source, such as collection, through a pipeline. Apipeline is a sequence of stream operations, which in this example isfilter
-map
-forEach
. In addition, aggregate operations typically accept lambda expressions as parameters, enabling you to customize how they behave.
For a more thorough discussion of aggregate operations, see theAggregate Operations lesson.
To process events in a graphical user interface (GUI) application, such as keyboardactions, mouse actions, and scroll actions, you typically createevent handlers, which usually involves implementing a particularinterface. Often, event handler interfaces are functionalinterfaces; they tend to have only one method.
In the JavaFX exampleHelloWorld.java
(discussed in the previous sectionAnonymous Classes), you canreplace the highlighted anonymous class with a lambda expression in thisstatement:
btn.setOnAction(new EventHandler<ActionEvent>() {@Overridepublic void handle(ActionEvent event) {System.out.println("Hello World!");}});
The method invocationbtn.setOnAction
specifies whathappens when you select the button represented by thebtn
object. This method requiresan object of typeEventHandler<ActionEvent>
. TheEventHandler<ActionEvent>
interface contains only one method,void handle(T event)
.This interface is a functional interface, so you could use the following highlighted lambda expression to replace it:
btn.setOnAction(event -> System.out.println("Hello World!") );
A lambda expression consists of the following:
Acomma-separated list of formal parameters enclosed inparentheses. TheCheckPerson.test
method contains one parameter,p
, which represents an instance of thePerson
class.
Note: You canomit the data type of the parameters in a lambda expression. Inaddition, you can omit the parentheses if there is only oneparameter. For example, the following lambda expression is alsovalid:
p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
The arrow token,->
Abody, which consists of a single expression or a statement block. This example uses the following expression:
p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
If youspecify a single expression, then the Java runtime evaluates theexpression and then returns its value. Alternatively, you can usea return statement:
p -> { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25;}
A return statement is not an expression; in a lambda expression,you must enclose statements in braces ({}
). However, you do not have to enclose a void method invocation in braces. For example, the following is a valid lambda expression:
email -> System.out.println(email)
Note that a lambda expression looks a lot like a methoddeclaration; you can consider lambda expressions as anonymousmethods—methods without a name.
The following example,Calculator
, is an example of lambda expressions that takemore than one formal parameter:
public class Calculator { interface IntegerMath { int operation(int a, int b); } public int operateBinary(int a, int b, IntegerMath op) { return op.operation(a, b); } public static void main(String... args) { Calculator myApp = new Calculator(); IntegerMath addition = (a, b) -> a + b; IntegerMath subtraction = (a, b) -> a - b; System.out.println("40 + 2 = " + myApp.operateBinary(40, 2, addition)); System.out.println("20 - 10 = " + myApp.operateBinary(20, 10, subtraction)); }}
The methodoperateBinary
performs amathematical operation on two integer operands. The operationitself is specified by an instance ofIntegerMath
. The example defines two operations with lambda expressions,addition
andsubtraction
. The example printsthe following:
40 + 2 = 4220 - 10 = 10
Like local and anonymous classes, lambda expressions cancapture variables; they have the same access to local variables of the enclosing scope. However, unlike local and anonymous classes, lambda expressions do not have any shadowing issues (seeShadowing for more information). Lambda expressions are lexically scoped. This means that they do not inherit any names from a supertype or introduce a new level of scoping. Declarations in a lambda expression are interpreted just as they are in the enclosing environment. The following example,LambdaScopeTest
, demonstrates this:
import java.util.function.Consumer; public class LambdaScopeTest { public int x = 0; class FirstLevel { public int x = 1; void methodInFirstLevel(int x) { int z = 2; Consumer<Integer> myConsumer = (y) -> { // The following statement causes the compiler to generate // the error "Local variable z defined in an enclosing scope // must be final or effectively final" // // z = 99; System.out.println("x = " + x); System.out.println("y = " + y); System.out.println("z = " + z); System.out.println("this.x = " + this.x); System.out.println("LambdaScopeTest.this.x = " + LambdaScopeTest.this.x); }; myConsumer.accept(x); } } public static void main(String... args) { LambdaScopeTest st = new LambdaScopeTest(); LambdaScopeTest.FirstLevel fl = st.new FirstLevel(); fl.methodInFirstLevel(23); }}
This example generates the following output:
x = 23y = 23z = 2this.x = 1LambdaScopeTest.this.x = 0
If you substitute the parameterx
in place ofy
in the declaration of the lambda expressionmyConsumer
, then the compiler generates an error:
Consumer<Integer> myConsumer = (x) -> { // ...}
The compiler generates the error "Lambda expression's parameter x cannot redeclare another local variable defined in an enclosing scope" because the lambda expression does not introduce a new level of scoping. Consequently, you can directly access fields, methods, and local variables of the enclosing scope. For example, the lambda expression directly accesses the parameterx
of the methodmethodInFirstLevel
. To access variables in the enclosing class, use the keywordthis
. In this example,this.x
refers to the member variableFirstLevel.x
.
However, like local and anonymous classes, a lambda expression can only access local variables and parameters of the enclosing block that are final or effectively final. In this example, the variablez
is effectively final; its value is never changed after it's initialized. However, suppose that you add the following assignment statement in the the lambda expressionmyConsumer
:
Consumer<Integer> myConsumer = (y) -> {z = 99; // ...}
Because of this assignment statement, the variablez
is not effectively final anymore. As a result, the Java compiler generates an error message similar to "Local variable z defined in an enclosing scope must be final or effectively final".
How do you determine the type of a lambda expression? Recallthe lambda expression that selected members who are male andbetween the ages 18 and 25 years:
p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
This lambda expression was used in the following two methods:
public static void printPersons(List<Person> roster, CheckPerson tester)
inApproach 3: Specify Search Criteria Code in a Local Class
public void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester)
inApproach 6: Use Standard Functional Interfaces with Lambda Expressions
When the Java runtime invokes the methodprintPersons
, it's expecting a data type ofCheckPerson
, so thelambda expression is of this type. However,when the Java runtime invokes the methodprintPersonsWithPredicate
,it's expecting a data type ofPredicate<Person>
,so the lambda expression is of this type. The data type thatthese methods expect is called thetarget type. To determine the type of a lambdaexpression, the Java compiler uses the target type of the contextor situation in which the lambda expression was found. It followsthat you can only use lambda expressions in situations in whichthe Java compiler can determine a target type:
Variable declarations
Assignments
Return statements
Array initializers
Method or constructor arguments
Lambda expression bodies
Conditional expressions,?:
Cast expressions
For method arguments, the Java compiler determines the targettype with two other language features: overload resolution andtype argument inference.
Consider the following two functional interfaces (java.lang.Runnable
andjava.util.concurrent.Callable<V>
):
public interface Runnable { void run();}public interface Callable<V> { V call();}
The methodRunnable.run
does not return a value, whereasCallable<V>.call
does.
Suppose that you have overloaded the methodinvoke
as follows(seeDefining Methods for more information about overloading methods):
void invoke(Runnable r) { r.run();}<T> T invoke(Callable<T> c) { return c.call();}
Which method will be invoked in the following statement?
String s = invoke(() -> "done");
The methodinvoke(Callable<T>)
will beinvoked because that method returns a value; the methodinvoke(Runnable)
does not. In this case, the type of the lambda expression() -> "done"
isCallable<T>
.
You canserialize a lambda expression if its target type and its captured arguments are serializable. However, likeinner classes, the serialization of lambda expressions is strongly discouraged.
About Oracle |Contact Us |Legal Notices |Terms of Use |Your Privacy Rights
Copyright © 1995, 2024 Oracle and/or its affiliates. All rights reserved.