Functional Interfaces

As mentioned earlier, many languages supporting lambda expressions make functions first-class citizens in the language. Java does not go that far - lambda expressions are still objects. In addition, even with the addition of lambda expressions, Java remains a statically typed language; therefore it needs to know the type of every argument (including the type of any lambda expressions) passed to a method at compile time. This leads to the obvious question: what type are lambda expressions?

The answer to this lies in a new Java 8 feature called functional interfaces. You have already come across interfaces with no methods (for instance, Serializable and Cloneable). These are known as marker or tagging interfaces, because they are simply used to denote something about a class without requiring it to implement anything.

Java now supports a second special class of interface that contain a single method: these can be designated as functional interfaces. Many existing Java interfaces have been designated as functional interfaces including the Comparable interface introduced earlier in the book.

//

These interfaces are also called Single Abstract Method interfaces (SAM Interfaces).

Prior to Java 8 there was nothing special about interfaces containing a single method: in Java 8 they can now act as the type for lambda expressions when tagged with the annotation @FunctionalInterface, for example:

@FunctionalInterface

public interface Example {

    void doSomething(Object obj);

}

One obvious use for lambda expressions is for callbacks. As discussed in the previous chapter, callbacks encapsulate a block of code that can be executed by another block of code when a specified event occurs. The event may be the user pressing a button on the GUI, or the result being received for a long running transaction.

The example below creates a functional interface to represent the printing of an object. Instances of this interface will then be passed as a callback to other methods, allowing them to print the results of an operation in the manner required by the initial invoker:

package printing;

@FunctionalInterface

public interface Printer {

    void print(Object obj);

}

Adding the @FunctionalInterface annotation ensures that the compiler will check that the interface meets the requirements for being a functional interface: if the interface contained anything other than one method definition the code would not compile.

Once a functional interface has been defined, you can declare a lambda expression of that type:

Printer printer =  (Object o) -> { System.out.println(">>>" + o); };

This line of code will not execute the lambda expression: it is simply assigning it to a variable so that it can be executed at a later point in the code.

If you think about this line of code, it is actually very unusual from a Java point of view. The compiler is determining the type of object to construct via the type it is assigned to. This is the other way round from how Java normally works: typically an expression has a type, and the compiler determines whether that is compatible with the type it is being assigned to.

If you wanted to, you could then invoke this as follows:

printer.print("Hello World");

Generally, however you want to pass the lambda expression to another method. The following is a contrived example, but shows the basic technique:

package lambda;

import printing.Printer;

public class Adder {

   

    public static void main(String[] args) {

        int num1 = 10;

        int num2 = 20;

        Printer printer =  (Object o) -> {

System.out.printf("The result of %d and %d is %d\n"

, num1, num2, o);

 };

       add(num1, num2, printer);

    }

   

    public static void add(int num1, int num2, Printer printer) {

        int result = num1 + num2;

        printer.print(result);

    }

}

This prints the following to standard output:

The result of 10 and 20 is 30

This is a contrived example because there is no reason for the add method not to return the result of the addition for the main method to print itself, but it provides a nice introduction to lambda expressions.

Notice that this lambda expression uses local variables from the main method, even though it is eventually executed in the add method: I will return to this subject shortly when discussing the scope that lambda expressions operate in.

As it happens, most of the time you do not need to define your own functional interfaces because Java provides a set for you. You can see this more clearly by moving to a realistic example.

A common use-case for lambda expressions is to have a method pass each element of a Collection to a lambda expression (just like the filter example last chapter). Java 8 provides a new method on List called forEach that accepts a lambda expression and implicitly passes each element in the List to it: the lambda expression it accepts must conform to the Consumer functional interface defined in the java.util.function package. This defines the following method:

void accept(T t)

The following shows a basic example:

package lambda;

import java.util.Arrays;

import java.util.List;

import java.util.function.Consumer;

 

public class Main {

    public static void main(String[] args) {

        Consumer c = (Object o) -> { System.out.println(">>>" + o); };

        List l = Arrays.asList("Hello", "World", "Print", "My", "List");

        l.forEach(c);

    }

}

This will produce the following output:

>>>Hello

>>>World

>>>Print

>>>My

>>>List

This approach is referred to as implicit rather than explicit iteration. Methods such as forEach remove the need for the programmer to describe the iteration process to the compiler – they implicitly perform it. This may sound like a minor convenience, but it will turn out to have powerful implications later in the book.

You can of course combine the declaration and use of the lambda expression into a single step:

l.forEach((Object o) -> { System.out.println(">>>" + o); });

In this case you don’t even need to import the Consumer interface – the compiler knows to check that the lambda expression conforms to the Consumer interface, because that is the interface accepted by the forEach method.

You can even take this one step further by removing the type definition from the lambda expression’s parameter, and remove the {} brackets from the lambda expression’s code block:

l.forEach(str ->  System.out.println(">>>" + str));

Because I used generics to declare that the List contains elements of type String, the compiler knows that every element in the array is a String. This also means that the compiler can safely assume that every element passed to the lambda expression will be a String.

This in turn allows you to omit the type of the str parameter, and simply use the following expression:

str ->  System.out.println(">>>" + str)

It should now be obvious that the filtering example from the previous chapter can be implemented with lambda expressions. Java 8 refers to a filter as a predicate and provides a functional interface called Predicate. This defines the following method:

boolean test(T t)

The fundamental aspect of a predicate is that it accepts an element and returns true or false. The List interface also supports a new method called removeIf. This method passes every element in the List to the predicate, and removes it if the predicate evaluates to true:

package lambda;

 

import java.util.Arrays;

import java.util.LinkedList;

import java.util.List;

 

public class Main2 {

    public static void main(String[] args) {

         List<Integer> l = new LinkedList

 (Arrays.asList(4,5,88,76,55,23,7,20,45));

        l.removeIf(i -> i%2 == 0);

        System.out.println(l);

    }

}

The expression i -> i%2 == 0 conforms to the Predicate interface because it accepts a single parameter and returns a Boolean result – as mentioned earlier, lambda expressions implicitly return a type if they consist of a single statement without {} brackets.

This could alternatively have been written as a code block:

l.removeIf(i -> {return i%2 == 0;});

I far prefer the first form for readability, and this is the form that is most commonly used.

I should probably mention at this point that the removeIf method is a very poor example of functional programming due to the fact that it mutates (or changes) the original list. Functional programming emphasizes immutability, and therefore the preferred approach would be to create a new list of odd numbers and leave the original list untouched. As you will see in the next chapter, it is possible to achieve this relatively simply with Java 8 using the Streams API.

In case you were wondering, the fact that this method mutates the List meant I could not simply use:

Arrays.asList(4,5,88,76,55,23,7,20,45)

The List created by asList is a fixed-length List mapped to the array specified, and therefore it is not possible to remove elements.

A Software Engineer Learns Java and Object Orientated Programming
titlepage.xhtml
part0000_split_000.html
part0000_split_001.html
part0000_split_002.html
part0000_split_003.html
part0000_split_004.html
part0000_split_005.html
part0000_split_006.html
part0000_split_007.html
part0000_split_008.html
part0000_split_009.html
part0000_split_010.html
part0000_split_011.html
part0000_split_012.html
part0000_split_013.html
part0000_split_014.html
part0000_split_015.html
part0000_split_016.html
part0000_split_017.html
part0000_split_018.html
part0000_split_019.html
part0000_split_020.html
part0000_split_021.html
part0000_split_022.html
part0000_split_023.html
part0000_split_024.html
part0000_split_025.html
part0000_split_026.html
part0000_split_027.html
part0000_split_028.html
part0000_split_029.html
part0000_split_030.html
part0000_split_031.html
part0000_split_032.html
part0000_split_033.html
part0000_split_034.html
part0000_split_035.html
part0000_split_036.html
part0000_split_037.html
part0000_split_038.html
part0000_split_039.html
part0000_split_040.html
part0000_split_041.html
part0000_split_042.html
part0000_split_043.html
part0000_split_044.html
part0000_split_045.html
part0000_split_046.html
part0000_split_047.html
part0000_split_048.html
part0000_split_049.html
part0000_split_050.html
part0000_split_051.html
part0000_split_052.html
part0000_split_053.html
part0000_split_054.html
part0000_split_055.html
part0000_split_056.html
part0000_split_057.html
part0000_split_058.html
part0000_split_059.html
part0000_split_060.html
part0000_split_061.html
part0000_split_062.html
part0000_split_063.html
part0000_split_064.html
part0000_split_065.html
part0000_split_066.html
part0000_split_067.html
part0000_split_068.html
part0000_split_069.html
part0000_split_070.html
part0000_split_071.html
part0000_split_072.html
part0000_split_073.html
part0000_split_074.html
part0000_split_075.html
part0000_split_076.html
part0000_split_077.html
part0000_split_078.html
part0000_split_079.html
part0000_split_080.html
part0000_split_081.html
part0000_split_082.html
part0000_split_083.html
part0000_split_084.html
part0000_split_085.html
part0000_split_086.html
part0000_split_087.html
part0000_split_088.html
part0000_split_089.html
part0000_split_090.html
part0000_split_091.html
part0000_split_092.html
part0000_split_093.html
part0000_split_094.html
part0000_split_095.html
part0000_split_096.html
part0000_split_097.html
part0000_split_098.html
part0000_split_099.html
part0000_split_100.html
part0000_split_101.html
part0000_split_102.html
part0000_split_103.html
part0000_split_104.html
part0000_split_105.html
part0000_split_106.html
part0000_split_107.html
part0000_split_108.html
part0000_split_109.html
part0000_split_110.html
part0000_split_111.html
part0000_split_112.html
part0000_split_113.html
part0000_split_114.html
part0000_split_115.html
part0000_split_116.html
part0000_split_117.html
part0000_split_118.html
part0000_split_119.html
part0000_split_120.html
part0000_split_121.html
part0000_split_122.html
part0000_split_123.html
part0000_split_124.html
part0000_split_125.html
part0000_split_126.html
part0000_split_127.html
part0000_split_128.html
part0000_split_129.html
part0000_split_130.html
part0000_split_131.html
part0000_split_132.html
part0000_split_133.html
part0000_split_134.html
part0000_split_135.html
part0000_split_136.html
part0000_split_137.html
part0000_split_138.html
part0000_split_139.html