11.3. Motivating Example

This section will focus on why we need this special syntax by comparing the lambda expression syntax to the process of creating a separate, named class (we will call this the class-based approach) we followed in the Interfaces Chapter.

Since our focus is on functional interfaces, we cannot use the PaymentProcessor Example from chapter 5 to compare lambda expression syntax to the named class syntax. Instead, we will use the functional interface, DiscountStrategy, introduced in the previous section and repeated below:

public interface DiscountStrategy {
   /**
    * Applies this discount strategy to the specified
    * {@code originalPrice} and returns the adjusted
    * price.
    *
    * @param originalPrice the original price of the item.
    * @return the discounted price.
    */
   double apply(double originalPrice);
} // apply

11.3.1. Implementing the Functional Interface

Test Yourself

Take a few minutes to review the process of Implementing an Interface and then create an implementing class called SeniorCitizenDiscount that has an apply method that reduces the original price by 10%.

Test Yourself Solution (Don’t open until completing the question above)

Your class should look something like this:

public class SeniorCitizenDiscount implements DiscountStrategy {
   @Override
   public double apply(double originalPrice) {
      return originalPrice * 0.9;
   } // apply
} // SeniorCitizenDiscount

Once we’ve created the SeniorCitizenDiscount class, we would also have to create an object of type SeniorCitizenDiscount in our application and then it to apply the discount. Of course, we would also have to modify our compilation script and check our dependencies to ensure the file would compile appropriately.

Below, we have two versions of a full program that implements the DiscountStrategy interface with two different types of discounts (senior citizen discount and student discount) and applies those discounts to a given price. Both programs produce identical output.

Note

As you read through the code below, we want you to pay attention to the notes above the code outlining the pros/cons of each approach and the length of the two programs. You should notice that the lambda version of the program is much shorter and confined to a single file, but uses syntax that you are currently unfamiliar with (more details to come).

The class-based approach that we learned about in the Interfaces Chapter can certainly be used to implement our interface. However, using this approach requires a separate .java file for each implementation. Since the interface has only one abstract method, there is a lot of overhead to create these two classes. We have to make the .java files, update our compilation script, copy (or retype) the class declaration and full signature of the method, etc. Lambda expressions help us avoid all of this.

Listing 11.1 In SeniorCitizensDiscount.java
1public class SeniorCitizenDiscount implements DiscountStrategy {
2
3   @Override
4   public double apply(double price) {
5      return price * 0.9;
6   } // apply
7
8} // SeniorCitizenDiscount
Listing 11.2 In StudentDiscount.java
 1public class StudentDiscount implements DiscountStrategy {
 2
 3   @Override
 4   public double apply(double price) {
 5      if (price > 2.00) {
 6         return price - 2.00;
 7      } // if
 8      return price;
 9   } // apply
10
11} // StudentDiscount
Listing 11.3 In Driver.java
 1public class Driver {
 2
 3    public static void main(String[] args) {
 4        double price = 10.95;
 5
 6        DiscountStrategy senior = new SeniorCitizenDiscount();
 7        DiscountStrategy student = new StudentDiscount();
 8
 9        double seniorPrice = senior.apply(price);
10        double studentPrice = student.apply(price);
11
12        System.out.printf("The senior price is %f\n", seniorPrice);
13        System.out.printf("The student price is %f\n", studentPrice);
14    } // main
15
16} // Driver

The lambda expressions defined in Driver2 fully implement the functional interface in the same way as the class-based approach without the need for separate classes. Here, we are providing the implementation for the single abstract method apply directly.

Listing 11.4 In Driver2.java
 1public class Driver2 {
 2
 3    public static void main(String[] args) {
 4        double originalPrice = 10.95;
 5
 6        DiscountStrategy senior = (price) -> {
 7            return price * 0.9;
 8        };
 9
10        DiscountStrategy student = (price) -> {
11            if (price > 2.00) {
12                return price - 2.00;
13            } // if
14            return price;
15        };
16
17        double seniorPrice = senior.apply(originalPrice);
18        double studentPrice = student.apply(originalPrice);
19
20        System.out.printf("The senior price is %f\n", seniorPrice);
21        System.out.printf("The student price is %f\n", studentPrice);
22    } // main
23
24} // Driver

In the following sections, we will further explain the lambda expression syntax and demonstrate how they can be used to implement other functional interfaces.