Filtering Criteria Defined by Anonymous Classes – Functional-Style Programming

Filtering Criteria Defined by Anonymous Classes

Example 13.3 uses anonymous classes to instantiate the criteria object, as shown at (1) and (2). The basic idea is that we can both declare and instantiate the class at the same time, where it is needed in the code, and in our case, as an argument in the call to the filterList() method. The type parameter E in this case is String. The anonymous classes at (1) and (2) provide implementations of the test() method for strings. The method is called at (8) to determine whether a String fulfills the selection criteria.

By using anonymous classes, we have avoided creating separate concrete classes, but the verbosity of declaring anonymous classes to encapsulate a single method is inescapable. And we still have to declare a new anonymous class for each selection criterion, duplicating a lot of boilerplate code.

Filtering Criteria Defined by Lambda Expressions

Ideally we would like to pass the code for the selection criteria as an argument to the filterList() method so that the method can apply the criteria to the elements in the list—that is, be able to change the behavior of the filterList() method depending on the selection criteria. This is an example of behavior parameterization.

Knowing that something is a Predicate<T>, all the information about the abstract method it implements can be inferred, as it is the only abstract method in the interface: its name, its parameters, any value it returns, and whether it throws any exceptions.

The assignment at (3) in Example 13.3 uses a lambda expression to provide an implementation for the parameterized Predicate<String> functional interface:

Click here to view code image

Predicate<String> predicate1 = str ->
        str.equals(new StringBuilder(str).reverse().toString());          // (3)

The reference predicate1 on the left-hand side is of type Predicate<String>, and it is assigned the value of the lambda expression on the right-hand side.

The lambda expression at (3) defines an anonymous function that takes a String as the only parameter, and returns a boolean value. Its type is String -> boolean. Recall that the test() method of the Predicate<String> functional interface type does exactly that. The function type of the Predicate<String> functional interface is also String -> boolean. The compiler can type check that the lambda expression is assignable to the reference on the left-hand side, since the expression represents an anonymous function that is compatible with the sole abstract method test() of the parameterized Predicate<String> interface.

The lambda expression at (3) is passed as an argument to the filterList() method via the reference predicate1 at (4). It is only executed when the test() method is called with a String argument in the filterList() method at (8).

The anonymous class declaration at (1):

Click here to view code image

new Predicate<String>() {
  @Override public boolean test(String str) {
    return str.equals(new StringBuilder(str).reverse().toString());
  }
}

is implemented by the lambda expression at (3):

Click here to view code image

str -> str.equals(new StringBuilder(str).reverse().toString())

Now we need only pass a new lambda expression to the filterList() method to filter a list based on selection criteria. Using lambda expressions is more precise, concise, and readable than using anonymous classes.

Later, when we discuss streams (Chapter 16, p. 879), we will also do away with the filterList() method for filtering lists.

Leave a Comment