On Github loskunos / objectivity-presentation-java8-lambda-and-streams
Grzegorz Kędzierski || gkedzierski@objectivity.co.uk || Objectivity 2014
...is very simple
What does these topics have in common?
so... are we going to talk about Functional Programming Paradigm?
Nope!
Just lambda and streams, ... FP is to big topic for this presentation ... and Java8 still isn't a functional language ... but more on that later.
interlude (I)
Imagin a world inhabited by Verbs and Nouns
... but only Nouns can move freely
Verbs can move only when they are escorted by Nouns
(or when they disguise themselves as nouns)
interlude (I) - cont’d
In such a world only Nouns are first-class citizens
Verbs live just to serve their masters
This is pre Java8 world
Did this changed in Java 8?
interlude (I) - cont’d
Nice (but a little bit outdated by Java8) "article" (short story?)about Nouns and Verbs in the Java Kingdom
Let's start with an example
...of non-lambda style of programming
button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println(e); } });
...what a waste of space (and time!)
and this has even a name - Boilerplate code!
Let's rewrite it with lambda on board!
button.addActionListener((ActionEvent e) -> { System.out.println(e); });
... simpler, but we can do even better, let remove parameter type!
button.addActionListener(e -> { System.out.println(e); });
...replace block with single statement!
button.addActionListener(e -> System.out.println(e));
...add method reference.
button.addActionListener(System.out::println);
Some examples (from spec) of Lambda expressions
() -> {} // No parameters; result is void () -> 42 // No parameters, expression body () -> { return 42; } // No parameters, block body with return () -> { // Complex block body with returns int result = 15; for (int i = 1; i < 10; i++) { result *= i; } return result; }
Some examples (from spec) of Lambda expressions
(int x) -> x+1 // Single declared-type parameter (x) -> x+1 // Single inferred-type parameter x -> x+1 // Parens optional for single inferred-type case (Thread t) -> { t.start(); } // Single declared-type parameter (int x, int y) -> x+y // Multiple declared-type parameters (x,y) -> x+y // Multiple inferred-type parameters (final int x) -> x+1 // Modified declared-type parameter (x, final y) -> x+y // Illegal: can't modify inferred-type parameters (x, int y) -> x+y // Illegal: can't mix inferred and declared types
Lambda syntax description
LambdaExpression: LambdaParameters '->' LambdaBody LambdaParameters: Identifier | '(' FormalParameterListopt ')' | '(' InferredFormalParameterList ')' LambdaBody: Expression | Block ParameterList: FullParameter {, FullParameter} FullParameter: {Annotation} [final] Type Identifier InferredFormalParameterList: Identifier {, Identifier}
In other words, lambda needs to know the context - or result type - to which it is assigned.
Little JVM digression - same lambda can be apply to different types, underneath it's using Java8's new ability called poly expression, which deduced type by looking at the target type.
The answer is:
Functional Interface is a funny buzzword for naming interfaces with only one "abstract" method.(abstract, because nowadays interfaces can have non-abstract default methods...)
In other words, all existing interfaces that have only one abstract methods can be now called functional interface, and we can used them as a result type for lambda expressions.
Fun fun = (int x, int y) -> x+y interface Fun { int sum(int a, int b) } // We can also ignore the result, but only when lambda is a block or // is an expressions with void type FunVoid funVoid = (int x, int y) -> { int i = x+y; } interface FunVoid { void sum(int a, int b) }
Because Java8 have type inference ability, we can also write
Fun fun = (x,y) -> x + y
And java will inference actual argument type, by looking at a result type.
In package java.util.function are definitions of somegeneric functional interfaces, like
Function: T -> R; Consumer: T -> void; Supplier: () -> T Predicate: T -> boolean BiFunction: T, U -> R BinaryOperator: T, T -> T
We can rewrite our previous example now using generic FI:
BinaryOperator<Integer> fun = (x,y) -> x + y;
Java8 provides annotation @FunctionalInterface which we can use to mark interface as functional interface, but this isn't necessary. We can use all interfaces, even those without this annotation.
When using this annotation, java compiler ensures that marked interface have only one abstract method.
In the world of Anonymous Classes, such classes can only access outer variables if those variables are marked as final.
Almost same applies to lambda expressions, but...
We don't have to marked them with final keyword.
Starting from Java8 all "variables or parameters whose value are never changed after they are initialized are effectively final".
Method reference is the ability to assign to functional interface a...method handler.
It looks like this:
System::getProperty // static method handler "abc"::length // instance method on instance handler System.out::println // instance method on instance handler String::length // instance method on class handler ArrayList::new // constructor handler handler
We need to assign handlers to some functional interfaces type, which should bound any necessary input parameters.
It's more easier than it sounds...
// static method - parameters and returning type same as in target method UnaryOperator<String> getProperty = System::getProperty; // instance method on instance - parameters and returning type same as in target method Supplier<Integer> length = "abc"::length; // instance method on instance Consumer<Object> printer = System.out::println; // instance method on class - first parameter is class instance, then parameters from target method Function<String,Integer> length2 = String::length; // constructor handler - parameters same as constructor, return type in same type hierarchy as class // (or any of its parents) Supplier<List> listFactory = ArrayList::new;
Strategy pattern
Template method pattern
Lazy evaluation of parameters
Writing DSLs
And a lot more!
In Java we could always passed code as data, but this "code" needs to be wrapped explicitly as Java Class (eg. Anonymous class).
Ability of creating lambdas in Java8 looks like syntactic sugar comparing to the the old notation.But underneath it uses different JVM mechanism... so no, it's NOT only a syntactic sugar.
Lambdas are compiled as static methods inside the class in which there are defined. Does this make a difference? Image a single class that use 100 lambdas inside. If we would compile them as nested classes, then we end up with 101 classes on classpath. Lambda will generate only one class. Nice.
interlude (II)
So... are Verbs first-class citizens in Java8 world?
Almost... but still, when going outside, Verbs must dress up as nouns(let's call them transgender verbs :P)
State of the Lambdahttp://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
State of the Lambda: Libraries Editionhttp://cr.openjdk.java.net/~briangoetz/lambda/lambda-libraries-final.html
Translation of Lambda Expressionshttp://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html
JSR 335: Lambda Expressions for the JavaTM Programming Languagehttps://jcp.org/en/jsr/detail?id=335
Let's start with simple iteration using for loop
List<String> strings = ... for (int i = 0; i < strings.size(); i++) { System.out.println(strings.get(0)); }
Starting from Java 1.5 we can rewrite it using for each loop
for (String s: strings) { System.out.println(s); }
This were the examples of external iterations - we, in code, decide how to iterate over some collection.
Next step - internal iteration
strings.forEach(System.out::println);
for (String s: strings) { System.out.println(s); }
Looks only like a small, meaningless syntactic change, but...
We are giving the control of the iteration to the library code and it changes a lot!
Our code moves from imperative style to more declarative one, so...
We are not telling how to iterate - library code decides how to do the iteration.
Library can support out of the box laziness, parallelism, out-of-order execution etc.
Client code is much cleaner, programmer needs to focus only on the business problem, not on the technical details of implementation.
is just a sequence of values on with we can manipulate and do common things, like filtering, mapping, reducing etc.
Manipulation is done via declarative style of programming and internal iterations.
All classes of this new API are located in package java.util.stream.
Because one example is worth a thousand of words...
It's realy simple, just...
stream.parallel()...
And thats all! Now all operation on stream will be executed in parallel.
But be careful!
Underneath Stream library is using Fork/Join framework for implementing parallelism.
...and it's is using a common thread pool from Fork/Join.
Having a long-running operation on Stream can block all threads in the pool!!!
To solve this problem we can either create a new pool and run stream operation in it
ForkJoinPool forkJoinPool = new ForkJoinPool(20); forkJoinPool.submit(() -> stream.parallel()... ).get();
or use a property to increase common thread pool size
java.util.concurrent.ForkJoinPool.common.parallelism
Source of this presentationhttps://github.com/loskunos/objectivity-presentation-java8-lambda-and-streams
Source of demo used during this presentationhttps://github.com/loskunos/objectivity-presentation-java8-lambda-and-streams-demo