Day 7 (abstracting the List type)

Mark comes up with some good news the next day – he will extend his business and sell other fruits as well as melons. This is cool, but our predicate only supports Melon instances.

So, how should we proceed to support other fruits too? How many other fruits? What if Mark decides to start selling another category of products, such as vegetables? We cannot simply create a predicate for each of them. This will take us back to the start.

The obvious solution is to abstract the List type. We start this by defining a new interface, and this time name it Predicate (remove Melon from the name):

@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}

Next, we rewrite the filterMelons() method and rename it as filter():

public static <T> List<T> filter(
List<T> list, Predicate<T> predicate) {

List<T> result = new ArrayList<>();

for (T t: list) {
if (t != null && predicate.test(t)) {
result.add(t);
}
}

return result;
}

Now, we can write filters for Melon:

List<Melon> watermelons = Filters.filter(
melons, (Melon m) -> "Watermelon".equalsIgnoreCase(m.getType()));

We can also do the same for numbers:

List<Integer> numbers = Arrays.asList(1, 13, 15, 2, 67);
List<Integer> smallThan10 = Filters
.filter(numbers, (Integer i) -> i < 10);

Take a step back and look at where we started and where we are now. The difference is huge thanks to Java 8 functional interfaces and lambda expressions. Have you noticed the @FunctionalInterface annotation on the Predicate interface? Well, that is an informative annotation type that's used to mark a functional interface. It is useful for an error to occur if the marked interface is not functional.

Conceptually, a functional interface has exactly one abstract method. Moreover, the Predicate interface that we've defined already exists in Java 8 as the java.util.function.Predicate interface. The java.util.function package contains 40+ such interfaces. Consequently, before defining a new one, it is advisable to check this package's content. Most of the time, the six standard built-in functional interfaces will do the job. These are listed as follows:

  • Predicate<T>
  • Consumer<T>
  • Supplier<T>
  • Function<T, R>
  • UnaryOperator<T>
  • BinaryOperator<T>

Functional interfaces and lambda expressions make a great team. Lambda expressions support the implementation of the abstract method of a functional interface directly inline. Basically, the entire expression is perceived as an instance of a concrete implementation of the functional interface, as demonstrated in the following code:

Predicate<Melon> predicate = (Melon m) 
-> "Watermelon".equalsIgnoreCase(m.getType());

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.38.92