Functional Interface: an interface with one abstract method
Lamda Expression
(int x, int y) → {...do something with x and y...}
Lamda vs Method Reference
Lamdas
- used to “pass functionality” as a parameter (create code block and pass reference as a pointer)
- parameters require brackets unless single parameter (with no type specified)
- if no parameters, requires: () ->
- if no code braces used, return of final statements is assumed
- if code braces used, must explicitly return if appropriate
Method References
- similar to lamda expressions
- used to assign existing code (such as String.startsWith()) to a functional interface
Static Methods | FI1 myLamda = x → Collections.sort(x); or FI1 myMethodRef = Collections::sort; |
Instance Methods | String myString = “ABC”; FI1 myLamda = s → myString.startsWith(s); or FI1 myMethodRef = myString::startsWith; |
Parameter Instance Methods | FI1 myLamda = s → s.isEmpty(); or FI1 myMethodRef = String::isEmpty; |
Constructors | FI1 myLamda = () → new ArrayList<String>(); or FI1 myMethodRef = ArrayList::new; |
Flavours of functional interface/Lamda
Predicate | Takes a parameter and returns true/false |
BiPredicate | Takes two parameters and returns true/false |
Consumer | Takes parameter, does work, returns nothing |
BiConsumer | Takes two parameters, does work, returns nothing |
Supplier | Takes NO parameters, generates / reads a value, returns the value |
Comparator | Takes parameters, compares them and returns the result |
Function | Takes parameters, does work, returns a value |
BiFunction | Takes two parameters, does work, returns a value |
UnaryOperator | As function but parameter and value of same type |
BinaryOperator | As BiFunction but parameters and value of same type |
- Checked exceptions do not work well with Functional Interfaces / Lamdas / Method Refs. Should not be thrown. Catch & thrown runtime exception instead.
- Lamdas can only reference ‘local variables’ if they are ‘effectively final’ – cannot change after Lamda defined. Can reference parameters, static & instance variables.
Chain / Link Operations
Consumer:
- consumer1.andThen(consumer2); // performs consumer1 and consumer2 on same input
Function:
- function1.andThen(function2); //performs consumer1 and consumer2 on same input
- function1.compose(function2); //performs consumer1 and passes output to consumer2
Predicate
- predicate1.and(predicate2);
- predicate1.or(predicate2);
- predicate1.negate(predicate2);
Optional
- Optional is a wrapper class that may or may not have a value
- Can call .isPresent() or .isEmpty() to find out
- Created by Optional.of(myValue) or Optional.empty()
- value can be retrieved by get() – exception if not present
- .orElse(myDefault) returns the value or a default if empty
Streams
Creation
Stream.empty();
Stream.of(1,2,3);
myCollection.stream();
Stream.generate(mySupplierFI); // infinite
Stream.iterate(initialValue, myUnaryOperator); // infinite
Stream.iterate(initialValue, myTerminationPredicate, myUnaryOperator);
Intermediate Operations
s.filter(myPredicate<T>); // drops items that fail predicate test and passes those that pass
s.distinct(); // uses .equals() to drop duplicate entries
s.limit(myCnt); // takes first myCnt entries
s.skip(myCnt); //skips myCnt entries
s.map(function<T,R>); // converts a stream of T to a stream of R
s.flatMap(function<T,Stream<R> >);
// explodes stream of Ts (with multiple subitems R) into a single stream of R
s.sorted();
s.sorted(comparator<T>);
s.peek(consumer<T>); //process each item and continue (non-terminating forEach)
Terminal Operations
s.count();
s.min(comparator<T>);
s.max(comparator<T>);
s.findFirst/Any();
s.anyMatch(predicate); // does any record match the predicate, returns boolean
s.allMatch(predicate); // do all records match the predicate, returns boolean
s.noneMatch(predicate); // do no records match the predicate, returns boolean
s.forEach(consumer); //executes consumer on each item and returns void
s.reduce(initialT, BinaryOp<T, T>);
// Starts with initialT, performs the binary operation on each item,
returns the final T
s.reduce(BinaryOp<T, T>);
// As above but returns Optional<T>
s.reduce(initialT, BiFunction<T, U>, BinaryOp<T, T>);
//starts with initialT, performs the binary function on each U
from stream to convert to T, BinaryOp to combine and returns the final T
s.collect(Supplier<R>, BiConsumer<R, T>, BiConsumer<R,R>);
// Supplier<R> provides ArrayList/StringBuilder to collect items in.
s.collect(Collector<T,A,R>);
// uses prebuilt collectors from class java.util.stream.Collectors
Available Collectors
- Collectors.joining(“,”); (concatenates values into a single string)
- Collectors.count(); (returns long)
- Collectors.maxBy(myComparator);
- Collectors.minBy(myComparator);
- Collectors.toList();
- Collectors.toSet();
- Collectors.toCollection(mySupplierOfCollection);
- Collectors.averagingInt/Long/Double();
- Collectors.summarizingInt/Long/Double();
- Collectors.toMap(…);
- Collectors.groupingBy(…);
- Collectors.partitioningBy(myPredicate, …); (split values into two classes using the predicate)
Lazy Evaluation
- Underlying data stream still updatable before stream executes
- Executes when it encounters a “terminal operation”
Primitive Streams
- Special streams for primitive types: IntStream, LongStream, DoubleStream
- Methods available to convert between types and to objects (e.g. .mapToDouble())
- Primitive functional interfaces also available (e.g. IntConsumer, DoublePredicate)