Skip to content

Functional Interfaces

trxe edited this page Oct 27, 2020 · 3 revisions

Functional Interfaces

We want functions to be first-class objects. With functional interfaces, this is possible.

Usually from (but not always) the package java.util.function, these interfaces have a single abstract method (SAM). We need these to specify the type of function that we pass into higher-order functions.

Commonly used Functional Interfaces (and their SAM)

Included with common type arguments (with wildcards).

Interface Method Commonly used with/in
Predicate<? extends T> test(T t) test(pred)
Function<? super T, ? extends U> apply(T t) map(f), flatmap(f)
BiFunction<? super T, ? super U, ? extends R> apply(T t, U u) combine(f), reduce(identity, f)
Supplier<T> get() lazily evaluated classes
Consumer<? super T> accept(T t) forEach(f)
Comparator<T> compare(T x, T y) compareTo(t), equals(t)

Implementing functional interfaces as method parameters

We have the method returning the largest integer of three.

public int max3(int x, int y, int z, Comparator<Integer> f) { ... }
  • single use class (nested in parent class)
class IntegerComparison implements Comparator<Integer> {
    public int compare(Integer x, Integer y) {
        return x - y;
    }
}
int largestOfThree = max3(a, b, c, new IntegerComparison());
```java
- anonymous class
```java
int largestOfThree = max3(a, b, c, new Comparator<Integer>() {
    public int compare(Integer x, Integer y) {return x - y;}}
);
  • lambda expression (N.B. only can be used with interfaces with SAM, as necessary for type inference)
    • types inferred from type argument in signature.
int largestOfThree = max3(a, b, c, (x, y) -> x - y); // least verbose
int largestOfThree = max3(a, b, c, (Integer x, Integer y) -> {return x - y;}); // most verbose
  • Method reference
int largestOfThree = max3(a, b, c, Integer::compare);

Lambda expressions and lexical (aka static) scoping

A lambda expression defines a new scope. Variables can be declared in that scope via the parameters, and aren't visible from the outside. However, variables outside the scope -- in enclosing scopes -- are visible within the lambda, unless they are overridden or modified.

In the expression (x, y) -> foo(x, y, z):

  • within lambda scope: x, y
  • within enclosing scope: foo, z

The variables outside the scope (the free variables) must not be modified, as they must be looked up from the closure object in the heap during evaluation.

BiFunction<Integer, Integer, Integer> bar(Integer z) {
    BiFunction<Integer, Integer, Integer> r = (x, y) -> ((x - y) / z); // z is in enclosing scope
    // z++; 
        // ^ Compile error; variable changed.
    // BiFunction<Integer, Integer, Integer> r = (x, y, z) -> ((x - y) / z); 
        // ^ Compile error, lambda parameter z cannot share names with enclosing scope parameter.
    return r;
}

bar(5).apply(25, 10); // returns 3

Function Composition

Two functions f and g can be composed using f.andThen(g) or g.compose(f) to get .

Note that the functions must be associative (i.e. )

Clone this wiki locally