FANDOM


This section discusses features included in Project Lambda, which aims to support programming in a multicore environment by adding closures and related features to the Java language. Project Lambda is part of the upcoming Java SE 8 release. Download the current JDK 8 snapshot that supports these features from Java Platform, Standard Edition 8 Early Access with Lambda Support.

One issue about anonymous classes is that if the implementation of your anonymous class is very simple, such as an interface that contains only one method, the syntax of anonymous classes may seem too 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. While this is often more concise than a named class, for classes with only one method, even an anonymous class seems a bit heavyweight. Lambda expressions let you express instances of single-method classes more compactly.

Ideal Use Case for Lambda ExpressionsEdit

Suppose 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. Suppose that members of this social networking application are represented by the following Member class:

public class Member {

     public enum Sex {

         MALE, FEMALE

     }

     String name;
     Date birthday;
     Sex gender;
     String emailAddress;

     public int getAge () {
         // ...
     }

     public void printMember () {
         // ...
     }
}

Suppose that the members of your social networking application are stored in a List<Member> instance.

This section begins with a naive approach to this use case. It improves upon this approach with local and anonymous classes, then finishes with an efficient and concise approach using lambda expressions. Find the code excerpts described in this section in the example RosterTest.

Approach 1: Create Methods that Searches for Members that Match One CharacteristicEdit

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 specified age:

   public static void printMembersOlderThan(List<member> roster, int age) {
   for (member p : roster) {
       if (p.getAge() >= age) {
           p.printmember();
       }
   }

}

Note: A List is an ordered Collection. A collection is an object that groups multiple elements into a single unit. Collections are used to store, retrieve, manipulate, and communicate aggregate data. For more information about collections, see the Collections trail.

This approach can potentially make your application brittle: Suppose you upgraded your application and changed the structure of the Member 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 you wanted to print members younger than a certain age, for example?

Approach 2: Create More Generalized Search MethodsEdit

The following method is more generic than printMembersOlderThan; it prints members within a specified range of ages:

   public static void printMembersWithinAgeRange(
   List<Member> roster, int low, int high) {
   for (Member p : roster) {
       if (low <= p.getAge() && p.getAge() > high) {
           p.printMember();
       }
   }

}

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 Member class and add other attributes such as relationship status or geographical location? Although this method is more generic than printMembersOlderThan, 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.

Approach 3: Specify Search Criteria Code in a Local ClassEdit

The following method does not contain any code that specifies search criteria:

   public void printMembers(
   List<Member> roster, CheckMember tester) {
   for (Member p : roster) {
       if (tester.test(p)) {
           p.printMember();
       }
   }

}

To specify the search criteria, you implement the CheckMember interface:

   interface CheckMember {
   boolean test(Member p);

}

Instead of creating a new class, however, you can create a local class that implements the CheckMember interface. The following class filters members that are eligible for Selective Service in the United States: those who are male and between the ages of 18 and 25:

   class CheckMemberEligibleForSelectiveService implements CheckMember {
   public boolean test(Member p) {
       return p.getGender() == Member.Sex.MALE &&
           p.getAge() >= 18 &&
           p.getAge() <= 25;
   }

}

To use this class, you create a new instance of it and invoke the printMembers method:

   RosterTest myRosterTest = new RosterTest();
   // ...
   myRosterTest.printMembers(
   roster, new CheckMemberEligibleForSelectiveService());
   System.out.println();

Although this approach is less brittle—you don't have to rewrite methods if you change the structure of the Member—you still have additional code: a new interface and a local class for each search you plan to perform in your application. Because CheckMemberEligibleForSelectiveService implements an interface, you can use an anonymous class instead and bypass the need to create a new local class for each search.

Approach 4: Specify Search Criteria Code in an Anonymous ClassEdit

One of the arguments of the following invocation of the method printMembers 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:

   myRosterTest.printMembers(
   roster, new CheckMember() {
   public boolean test(Member p) {
       return p.getGender() == Member.Sex.MALE &&
           p.getAge() >= 18 &&
           p.getAge() <= 25;
       }
   }

);

This approach reduces the amount of code required—you don't have to create a new class for each search you want to perform. However, the syntax of anonymous classes is bulky considering that the CheckMember interface contains only one method. In this case, you can use a lambda expression of an anonymous class, as described in the next section.

Approach 5: Specify Search Criteria Code with a Lambda ExpressionEdit

The CheckMember interface is a functional interface. A functional interface is any interface that contains only one method. Because a functional interface contains only one method, you can omit the name of that method when you implement it. To do this, instead of using an anonymous class expression, you use a lambda expression, which is highlighted in the following method invocation:

   myRosterTest.printMembers(
   roster,
   (Member p) -> p.getGender() == Member.Sex.MALE &&
       p.getAge() >= 18 &&
       p.getAge() <= 25
   );

See Syntax of Lambda Expressions for information on how to define lambda expressions.

We can use a standard functional interface in place of the interface CheckMember, which reduces even further the amount of code required.

Approach 6: Use Standard Functional Interfaces with Lambda ExpressionsEdit

Let's revisit the CheckMember interface:

   interface CheckMember {
   boolean test(Member p);

}

This is a very simple interface. It's a functional interface because it contains only one method. This method takes one parameter and returns a boolean value. The method is so simple that it might not be worth it to define one in your application. Consequently, Java SE defines several standard functional interfaces, which you can find in the package java.util.functions.

For example, you can use the Predicate<T> interface in place of CheckMember. This interface contains the method boolean test(T t):

   interface Predicate<T> {
   boolean test(T t);

}

The interface Predicate<T> is an example of a generic interface. (For more information about generics, see the Generics (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 instantiate a class that implements a generic interface, you substitute its type parameters with any type.

Suppose you substitute the type parameter T with the type Member in the interface Predicate<T> as follows:

   interface Predicate<Member> {
   boolean test(Member t);

}

The result is an interface that has the same type as CheckMember that contains a method that has the same return type and parameters as CheckMember.boolean test(Member p). Consequently, you can use Predicate<T> in place of CheckMember as the following method demonstrates:

   public void printMembersWithPredicate(
   List<Member> roster, Predicate<Member> tester) {
   for (Member p : roster) {
       if (tester.test(p)) {
           p.printMember();
       }
   }

}

As a result, the following method invocation is the same as when we invoked printMembers to obtain members who are eligible for Selective Service:

   myRosterTest.printMembersWithPredicate(
   roster,
   p -> p.getGender() == Member.Sex.MALE &&
       p.getAge() >= 18 &&
       p.getAge() <= 25

);

This is not the only place in this method we could use a lambda expression. The following approach looks for other ways to use lambda expressions.

Approach 7: Use Lambda Expressions Throughout Your ApplicationEdit

Lambda expressions enable you to pass a method as a parameter to another method. Let's revisit the method printMembersWithPredicate to see where else we could use lambda expressions:

   public void printMembersWithPredicate(
   List<Member> roster, Predicate<Member> tester) {
   for (Member p : roster) {
       if (tester.test(p)) {
           p.printMember();
       }
   }

}

You can replace the action performed on each Member instance, in this example, p.printMember(), with a lambda expression. Remember, to use a lambda expression, you need to implement a functional interface. In this case, you need a functional interface that contains a method that takes one argument (an object of type Member) and returns void. The Block<T> interface contains the method void apply(T t). The following method replaces the invocation p.printMember() with an instance of Block<Member>:

   public void processMembers(
   List<Member> roster,
   Predicate<Member> tester,
   Block<Member> block) {
   for (Member p : roster) {
       if (tester.test(p)) {
           block.apply(p);
       }
   }

}

As a result, the following method invocation is the same as when we invoked printMembers to obtain members who are eligible for Selective Service. The lambda expression used to print members is highlighted:

   myRosterTest.processSelectedMembers(
   roster,
   p -> p.getGender() == Member.Sex.MALE &&
       p.getAge() >= 18 &&
       p.getAge() <= 25,
   p -> p.printMember()

);

Note that although p.printMember() is a statement, not an expression, you do not have to enclose it in curly braces ({}). You can use a void method invocation as the body of a lambda expression.

What if you wanted to do more with your members' profiles instead of printing them out, such as validate their profiles or retrieve their contact information? In this case, you need a functional interface that contains a method that returns a value. The Mapper<R,T> interface contains the method R map(T t). The following method retrieves the data specified by the parameter mapper, and then performs an action on it specified by the parameter block:

   public void processMembersWithMapper(
   List<Member> roster,
   Predicate<Member> tester,
   Mapper<String, Member> mapper,
   Block<String> block) {
   for (Member p : roster) {
       if (tester.test(p)) {
           String data = mapper.map(p);
           block.apply(data);
       }
   }

}

The following method retrieves the e-mail address from each member contained in roster and then prints it:

   myRosterTest.processSelectedMembersWithMapper(
   roster,
   p -> p.getGender() == Member.Sex.MALE &&
       p.getAge() >= 18 &&
       p.getAge() <= 25,
   p -> p.getEmailAddress(),
   email -> System.out.println(email)

);

Lambda Expressions in GUI ApplicationsEdit

To process events in a 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 HelloWorld.java discussed in the previous section Anonymous Classes, you can replace the anonymous class with a lambda expression in this statement:

       btn.setOnAction(new EventHandler<ActionEvent>() {
           @Override
           public void handle(ActionEvent event) {
               System.out.println("Hello World!");
           }
       });

The method invocation btn.setOnAction specifies what happens when you select the button represented by the btn object. This method requires an object of type EventHandler<ActionEvent>. The EventHandler<ActionEvent> interface contains only one method, void handle(T event). This interface is a functional interface, so you could use a lambda expression:

       btn.setOnAction(
         event -> System.out.println("Hello World!")
       );

Syntax of Lambda ExpressionsEdit

A lambda expression consists of the following:

  • A comma-separated list of formal parameters enclosed in parentheses; the CheckMember.test method contains one parameter, p, which represents an instance of the Member class.

Note: You may omit the data type of the parameters in a lambda expression. In addition, you may omit the parentheses if there is only one parameter. For example, the following lambda expression is also valid:

   p -> p.getGender() == Member.Sex.MALE &&
   p.getAge() >= 18 &&
   p.getAge() <= 25
  • The arrow token, ->
  • A body, which consists of a single expression or a statement block; this example uses this expression:
   p.getGender() == Member.Sex.MALE &&
   p.getAge() >= 18 &&
   p.getAge() <= 25

If you specify a single expression, the Java runtime evaluates the expression and then returns its value. Alternatively, you can use a return statement:

   p -> {
   return p.getGender() == Member.Sex.MALE
       && p.getAge() >= 18
       && p.getAge() <= 25;

}

Note that a return statement is not an expression; in a lambda expression, you must enclose statements in curly braces ({}). However, you do not have to enclose a void method invocation in curly 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 method declaration; you can consider lambda expressions as anonymous methods—methods without a name.

The following example, Calculator is an example of lambda expressions that take more 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 method operateBinary performs a mathematical operation on two integer operands. The operation itself is specified by an instance of IntegerMath. The example defines two operations with lambda expressions, addition and subtraction. The example prints the following:

40 + 2 = 42 20 - 10 = 10

Accessing Local Variables of the Enclosing ScopeEdit

Lambda expressions have the same access to local variables of the enclosing scope as local and anonymous classes. 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 nor 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.functions.Block;
   public class LambdaScopeTest {
   public int x = 0;
   class FirstLevel {
       public int x = 1;
       void methodInFirstLevel(int x) {
           
           // The following statement causes the compiler to generate
           // the error "local variables referenced from a lambda expression
           // must be final or effectively final" in statement A:
           //
           // x = 99;
           
           Block<Integer> myBlock = (y) -> 
           {
               System.out.println("x = " + x); // Statement A
               System.out.println("y = " + y);
               System.out.println("this.x = " + this.x);
               System.out.println("LambdaScopeTest.this.x = " +
                   LambdaScopeTest.this.x);
           };
           myBlock.apply(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 this.x = 1 LambdaScopeTest.this.x = 0

If you substitute the parameter x in place of y in the declaration of the lambda expression myBlock, the compiler generates an error:

Block<Integer> myBlock = (y) -> {

   // ...

}

The compiler generates the error, "variable x is already defined in method methodInFirstLevel(int)" 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 x of the method methodInFirstLevel. To access variables in the enclosing class, use the keyword this. In this example, this.x refers to the member variable FirstLevel.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. For example, suppose you add the following assignment statement immediately after the methodInFirstLevel definition statement as follows:

   void methodInFirstLevel(int x) {
   x = 99;
   // ...

}

Because of this assignment statement, the variable FirstLevel.x is not effectively final anymore. As a result, the Java compiler generates an error message similar to "local variables referenced from a lambda expression must be final or effectively final" where the lambda expression myBlock tries to access the FirstLevel.x variable:

   System.out.println("x = " + x);

Target TypingEdit

How 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() == Member.Sex.MALE
   && p.getAge() >= 18
   && p.getAge() <= 25

Recall that we used the same lambda expression in the following two methods:

  • public void printMembers(List<Member> roster, CheckMember tester)
  • public void printMembersWithPredicate(List<Member> roster, Predicate<Member> tester)

When the Java runtime invokes the method printMembers, it's expecting a data type of CheckMember, so the lambda expression is of this type. However, when the Java runtime invokes the method printMembersWithPredicate, it's expecting a data type of Predicate<Member>, so the lambda expression is of this type. The data type that these methods expect is called the target type. To determine the type of a lambda expression, the Java compiler uses the target type of the context or situation in which the lambda expression was found. It follows that you can only use lambda expressions in situations in which the 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

Target Types and Method ArgumentsEdit

For 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, java.lang.Runnable, and java.util.concurrent.Callable<V>:

public interface Runnable {

   void run();

}

public interface Callable<V> {

   V call();

}

The method Runnable.run does not return a value while Callable<V>.call does.

Suppose you have overloaded the method invoke as follows. (Overloaded methods are those that have the same name, belong to the same class, but have different parameter lists or return values. See Defining Methods for more information):

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 invoke(Callable<T>) will be invoked because that method returns a value; the method invoke(Runnable) does not. In this case, the type of the lambda expression () -> "done" is Callable<T>.

Community content is available under CC-BY-SA unless otherwise noted.