GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||
GeezerVison is showing docs.oracle.com — Lambda Expressions (The Java™ Tutorials > Learning the Java Language > Classes and Objects) Documentation
The Java™ Tutorials
Hide TOC
Lambda Expressions
Trail: Learning the Java Language
Lesson: Classes and Objects Section: Nested Classes 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. Lambda ExpressionsOne 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 classes with only one method, even an anonymous class seems a bit excessive and cumbersome. Lambda expressions let you express instances of single-method classes more compactly. This section covers the following topics:
Ideal Use Case for Lambda ExpressionsSuppose that you are creating a social networking application. You want to create a feature that enables an administrator to perform any kind of action, such as sending a message, on members of the social networking application that satisfy certain criteria. The following table describes this use case in detail:
Suppose that members of this social networking application are
represented by the following
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 application
are stored in a 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 example
Approach 1: Create Methods That Search for Members That Match One CharacteristicOne 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 specified age: public static void printPersonsOlderThan(List<Person> roster, int age) {
for (Person p : roster) {
if (p.getAge() >= age) {
p.printPerson();
}
}
}
Note: A
This approach can potentially make your application brittle, 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 the Approach 2: Create More Generalized Search MethodsThe following method is more generic than 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 the Approach 3: Specify Search Criteria Code in a Local ClassThe 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 each To specify the search criteria, you implement the
interface CheckPerson {
boolean test(Person p);
}
The following class implements the 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 new
instance of it and invoke the printPersons(
roster, new CheckPersonEligibleForSelectiveService());
Although this approach is less brittle—you don't have to rewrite methods if you change the structure of the Approach 4: Specify Search Criteria Code in an Anonymous ClassOne of the arguments of the following invocation of the method 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 the Approach 5: Specify Search Criteria Code with a Lambda ExpressionThe printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
See Syntax of Lambda Expressions for information about how to define lambda expressions. You can use a standard functional interface in place of the interface Approach 6: Use Standard Functional Interfaces with Lambda ExpressionsReconsider the interface CheckPerson {
boolean test(Person p);
}
This is a very
simple interface. It's a functional interface because it contains
only one abstract method. This method takes one parameter and returns a
For example, you can use the
interface Predicate<T> {
boolean test(T t);
}
The interface interface Predicate< This parameterized type contains a method that has the same return type and parameters as 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 invoked
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. Approach 7: Use Lambda Expressions Throughout Your ApplicationReconsider the method public static void printPersonsWithPredicate(
List<Person> roster, Predicate<Person> tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
This method checks each Instead of invoking the method 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 a
result, the following method invocation is the same as when you invoked 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 retrieve
their contact information? In this case, you need a functional
interface that contains an abstract method that returns a value.
The
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 member
contained in processPersonsWithFunction(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25,
p -> p.getEmailAddress(),
email -> System.out.println(email)
);
Approach 8: Use Generics More ExtensivelyReconsider the method 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 the 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:
You can replace each of these actions with an aggregate operation. Approach 9: Use Aggregate Operations That Accept Lambda Expressions as ParametersThe following example uses aggregate operations to print the e-mail addresses of those members contained in the collection 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 method
The operations For a more thorough discussion of aggregate operations, see the Aggregate Operations lesson. Lambda Expressions in GUI ApplicationsTo process events in a graphical user interface (GUI) application, such as keyboard actions, mouse actions, and scroll actions, you typically create event handlers, which usually involves implementing a particular interface. Often, event handler interfaces are functional interfaces; they tend to have only one method. In the JavaFX example
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
The method invocation btn.setOnAction(
event -> System.out.println("Hello World!")
);
Syntax of Lambda ExpressionsA lambda expression consists of the following:
Note that a lambda expression looks a lot like a method declaration; you can consider lambda expressions as anonymous methods—methods without a name. The following example,
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 method 40 + 2 = 42 20 - 10 = 10 Accessing Local Variables of the Enclosing ScopeLike local and anonymous classes, lambda expressions can
capture 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 (see
Shadowing 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,
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 = 23 y = 23 z = 2 this.x = 1 LambdaScopeTest.this.x = 0 If you substitute the parameter 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 parameter 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 variable Consumer<Integer> myConsumer = (y) -> {
z = 99;
// ...
}
Because of this assignment statement, the variable Target TypingHow do you determine the type of a lambda expression? Recall the lambda expression that selected members who are male and between 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:
When the Java runtime invokes the method
Target Types and Method ArgumentsFor method arguments, the Java compiler determines the target type with two other language features: overload resolution and type argument inference. Consider the following two functional interfaces (
public interface Runnable {
void run();
}
public interface Callable<V> {
V call();
}
The method Suppose that you have overloaded the method 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 method SerializationYou can serialize a lambda expression if its target type and its captured arguments are serializable. However, like inner 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.
Previous page: Anonymous Classes
Next page: Method References | ||||||||||||||||||||||||||
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |