Functional

Java logo 

Agenda

  • Functional programming in Java
  • Exercises 1
  • Working with Streams
  • Exercises 2
  • Working with Optionals
  • Exercises 3

Functional Programming in Java

Anonymous classes vs Functional interfaces

Anonymous classes – the old way of functional programming in Java


Collections.sort(words, new Comparator<String> {
    public int compare(String s1, String s2) {
        return Integer.compare(s1.length(), s2.length());
    }
});
					

Anonymous classes vs Functional interfaces

Functional interface – new way of functional programming in Java


Collections.sort(words, (s1, s2) ->
    Integer.compare(s1.length(), s2.length()));
					

Functional interfaces

  • How come we don’t have to specify the type?
  • When should we specify the type?
    • When it helps to understand/document the code
    • When compiler reports a type inference error (then we have to specify it)

Functional interfaces with enums


public enum Operation {
    PLUS("+") {
        public double apply(double x, double y) { return x + y; }
    };
    MINUS("-") {
        public double apply(double x, double y) { return x - y; }
    };
    MULTIPLY("*") {
        public double apply(double x, double y) { return x * y; }
    };
    DIVIDE("/") {
        public double apply(double x, double y) { return x / y; }
    };

    private final String symbol;
    Operation(String symbol) { this.symbol = symbol; }

    @Override
    public String toString() { return symbol; }

    public abstract double apply(double x, double y);
}
					

Functional interfaces with enums


public enum Operation {
    PLUS("+", (x, y) -> x + y );
    MINUS("-", (x, y) -> x - y );
    MULTIPLY("*", (x, y) -> x * y );
    DIVIDE("/", (x, y) -> x / y );

    private final String symbol;
    private final DoubleBinaryOperator op;
    Operation(String symbol, DoubleBinaryOperator op) {
        this.symbol = symbol;
        this.op = op;
    }

    @Override
    public String toString() { return symbol; }

    public double apply(double x, double y) {
        return op.applyAsDouble(x, y);
    }
}
					

Lambda syntax

Examples

			
// no arguments
() -> System.out.println("Hello")	
// one argument
s -> System.out.println(s)
// two arguments
(a, b) -> a + b
// explicit argument types
(Integer a, Integer b) -> a + b
// multiple statements
(a, b) -> {
	System.out.println(a);
	System.out.println(b);
	return a + b;
}
			
			

Summary

  • Don’t use lambdas everywhere – they are meant to ease the work. If they are too complicated to understand use classic/iterative approach
  • Use types only when necessary, let your compiler do the work
  • Specify type for data that you want to process with lambdas – it’ll help the compiler to infer the type inside

Method references

Method references are even shorter way to write some expressions than lambdas

lambda:


map.merge(key, 1, (count, incr) -> count + incr);
					

method reference:


map.merge(key, 1, Integer::sum);
					

Method references

  • The more parameters are used, the more code can be shortened with the usage of method references
  • Lambdas, however, can be self-documenting and easier to understand even it they are longer
  • If the lambda takes too much space and makes code hard to understand it can be extracted to another method which then can be used as a method reference

Types of method references

Reference to a Static Method

method reference:


Integer::parseInt
					

lambda:


string -> Integer.parseInt(string)
					

Types of method references

Reference to an Instance Method of a specific object

method reference:


Instant.now()::isAfter
					

lambda:


Instant then = Instant.now();
t -> then.isAfter(then)
					

Types of method references

Reference to an Instance Method of an unspecified object

method reference:


String::toLowerCase()
					

lambda:


string -> string.toLowerCase()
					

Types of method references

Constructor of an object

method reference:


HashMap<K,V>::new
					

lambda:


() -> new HashMap<K,V>
					

Types of method references

Constructor of an array

method reference:


int[]::new
					

lambda:


length -> new int[length]
					

Standard Functional interfaces

java.util.function

Standard Functional Interfaces

UnaryOperator<T>

Signature:


T apply(T t)
					

Example:


String::toLowerCase
					

Standard Functional Interfaces

BinaryOperator<T>

Signature:


T apply(T t1, T t2)
					

Example:


BigInteger:add
					

Standard Functional Interfaces

Predicate<T>

Signature:


boolean test(T t)
					

Example:


Collection::isEmpty
					

Standard Functional Interfaces

Function<T,R>

Signature:


R apply(T t)
					

Example:


Arrays::asList
					

Standard Functional Interfaces

Supplier<T>

Signature:


T get()
					

Example:


Instant::now
					

Standard Functional Interfaces

Consumer<T>

Signature:


void accept(T t)
					

Example:


System.out::println
					

Writing your own functional interfaces

  • When to write you own Functional Interface?
  • Usage of @FunctionalInterface

example of use of @FunctionalInterface
					

functional interfaces exercises

Streams

  • Consist of two abstractions: Stream and stream’s flow
  • They allow to perform multi-step, consecutive operations on data processed in stream’s flow
  • Typical stream consists of a source stream, (optional) intermediate operations and final operation
  • Lazy

Contents of a stream

  • beginning of a stream: stream, parallelStream
  • intermediate functions: map, filter, flatMap, limit, skip, ...
  • terminal functions: collect, count, max, min, findFirst, findAny, anyMatch, ...

When to use Streams

  • When not to use streams?
  • When to use streams?

Avoid side effects in Streams

  • What are pure functions and why they should be used?
  • Problem with exceptions in streams

Parallel Streams

  • How they work?
  • What are the benefits and pitfalls of parallel streams usage?
  • Why you should be careful when using parallel streams?

Examples of how to use streams

go to code

exercises for streams

Optionals

Optional<T>

  • What are optionals
  • Why returning a null within an optional is a mistake
  • Optionals are not serializable
  • Optionals should not be used everywhere

Operators provided with Optional API

  • filter, map, flatMap
  • orElse() vs orElseGet()

Optional API changes

JDK 9

  • ifPresentOrElse()
  • or()
  • stream()

JDK 10

  • orElseThrow()

JDK 11

  • isEmpty()

examples of how to use optionals and common mistakes

  • use of isPresent() instead of ifPresent()

Optional<Movie> movie = Optional.of(library.findMovie(id));
if (movie.isPresent()) {
    someLogic;
    }
return movie;
					

Optional<Movie> movie = Optional.of(library.findMovie(id));
movie.ifPresent(() -> someLogic );
return movie;
					

examples of how to use optionals and common mistakes

  • passing Optional as a parameter

public static List<Person> search(List<Person> people, Optional<Integer> age) {
    // Null checks for people and name
    return people.stream()
            .filter(p -> p.getAge().get() >= age.orElse(0))
            .collect(Collectors.toList());
}
					

public static List<Person> search(List<Person> people, Integer age) {
    // Null checks for people and name
    final Integer ageFilter = age != null ? age : 0;

    return people.stream()
            .filter(p -> p.getAge().get() >= ageFilter)
            .collect(Collectors.toList());
}
					

examples of how to use optionals and common mistakes

  • using flatMap + map for streamsOfOptionals

streamOfOptionals
    .filter(Optional::isPresent)
    .map(Optional::get)
					

streamOfOptionals
    .flatMap(Optional::stream)
					

Any Questions?

exercises for optionals

thank you very much!