Functional Interfaces - nus-cs2030/2021-s1 GitHub Wiki
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.
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) |
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);
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
Two functions f
and g
can be composed using f.andThen(g)
or g.compose(f)
to get .