Top Banner
Lambda Expressions In JDK8 Going Beyond The Basics Simon Ritter Head of Java Technology Evangelism Oracle Corp. Twitter: @speakjava Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
44
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Lambdas : Beyond The Basics

Lambda Expressions In JDK8Going Beyond The Basics

Simon RitterHead of Java Technology EvangelismOracle Corp.

Twitter: @speakjava

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Page 2: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Safe Harbor Statement

The following is intended to outline our general product direction. It is intended for information purposes only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. The development, release, and timing of any features or functionality described for Oracle’s products remains at the sole discretion of Oracle.

2

Page 3: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Program Agenda

Lambdas And Streams Primer

Delaying Execution

Avoiding loops in Streams

The Art of Reduction

Conclusions

1

2

3

4

5

Oracle Confidential – Internal/Restricted/Highly Restricted 3

Page 4: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Lambdas And Streams Primer

Page 5: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Lambda Expressions In JDK8

• Old style, anonymous inner classes

• New style, using a Lambda expression

5

Simplified Parameterised Behaviour

new Thread(new Runnable {public void run() {

doSomeStuff();}

}).start();

new Thread(() -> doSomeStuff()).start();

Page 6: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Lambda Expressions

• Lambda expressions represent anonymous functions

– Same structure as a method• typed argument list, return type, set of thrown exceptions, and a body

– Not associated with a class

• We now have parameterised behaviour, not just values

Some Details

double highestScore = students.

filter(Student s -> s.getGradYear() == 2011).

map(Student s -> s.getScore())

max();

What

How

Page 7: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Lambda Expression Types

• Single-method interfaces are used extensively in Java

– Definition: a functional interface is an interface with one abstract method

– Functional interfaces are identified structurally

– The type of a lambda expression will be a functional interface• Lambda expressions provide implementations of the abstract method

interface Comparator<T> { boolean compare(T x, T y); }

interface FileFilter { boolean accept(File x); }

interface Runnable { void run(); }

interface ActionListener { void actionPerformed(…); }

interface Callable<T> { T call(); }

Page 8: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Local Variable Capture

• Lambda expressions can refer to effectively final local variables from the surrounding scope

– Effectively final: A variable that meets the requirements for final variables (i.e., assigned once), even if not explicitly declared final

– Closures on values, not variables

void expire(File root, long before) {

root.listFiles(File p -> p.lastModified() <= before);

}

Page 9: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

What Does ‘this’ Mean In A Lambda

• ‘this’ refers to the enclosing object, not the lambda itself

• Think of ‘this’ as a final predefined local

• Remember the Lambda is an anonymous function

– It is not associated with a class

– Therefore there can be no ‘this’ for the Lambda

Page 10: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Referencing Instance VariablesWhich are not final, or effectively final

class DataProcessor {

private int currentValue;

public void process() {

DataSet myData = myFactory.getDataSet();

dataSet.forEach(d -> d.use(currentValue++));

}

}

Page 11: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Referencing Instance VariablesThe compiler helps us out

class DataProcessor {

private int currentValue;

public void process() {

DataSet myData = myFactory.getDataSet();

dataSet.forEach(d -> d.use(this.currentValue++);

}

}

‘this’ (which is effectively final) inserted by the compiler

Page 12: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Type Inference

• The compiler can often infer parameter types in a lambda expression

Inferrence based on the target functional interface’s method signature

• Fully statically typed (no dynamic typing sneaking in)

–More typing with less typing

List<String> list = getList();Collections.sort(list, (String x, String y) -> x.length() - y.length());

Collections.sort(list, (x, y) -> x.length() - y.length());

static T void sort(List<T> l, Comparator<? super T> c);

Page 13: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Functional Interface Definition

• An interface

• Must have only one abstract method

– In JDK 7 this would mean only one method (like ActionListener)

• JDK 8 introduced default methods

– Adding multiple inheritance of types to Java

– These are, by definition, not abstract (they have an implementation)

• JDK 8 also now allows interfaces to have static methods

– Again, not abstract

• @FunctionalInterface can be used to have the compiler check

13

Page 14: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Is This A Functional Interface?

14

@FunctionalInterfacepublic interface Runnable {

public abstract void run();}

Yes. There is only one abstract method

Page 15: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Is This A Functional Interface?

15

@FunctionalInterfacepublic interface Predicate<T> {

default Predicate<T> and(Predicate<? super T> p) {…};default Predicate<T> negate() {…};default Predicate<T> or(Predicate<? super T> p) {…};static <T> Predicate<T> isEqual(Object target) {…};boolean test(T t);

}

Yes. There is still only one abstract method

Page 16: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Is This A Functional Interface?

16

@FunctionalInterfacepublic interface Comparator {

// Static and default methods elidedint compare(T o1, T o2);boolean equals(Object obj);

}

The equals(Object) method is implicit from the Object class

Therefore only oneabstract method

Page 17: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Stream Overview

• A stream pipeline consists of three types of things

– A source

– Zero or more intermediate operations

– A terminal operation• Producing a result or a side-effect

Pipeline

int total = transactions.stream().filter(t -> t.getBuyer().getCity().equals(“London”)).mapToInt(Transaction::getPrice).sum();

Source

Intermediate operation

Terminal operation

Page 18: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Stream Sources

• From collections and arrays

– Collection.stream()

– Collection.parallelStream()

– Arrays.stream(T array) or Stream.of()

• Static factories

– IntStream.range()

– Files.walk()

• Roll your own

– java.util.Spliterator

Many Ways To Create

Page 19: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Optional Class

• Terminal operations like min(), max(), etc do not return a direct result

• Suppose the input Stream is empty?

• Optional<T>– Container for an object reference (null, or real object)

– Think of it like a Stream of 0 or 1 elements

– use get(), ifPresent() and orElse() to access the stored reference

– Can use in more complex ways: filter(), map(), etc• gpsMaybe.filter(r -> r.lastReading() < 2).ifPresent(GPSData::display);

Helping To Eliminate the NullPointerException

Page 20: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Lambda Expressions andDelayed Execution

Page 21: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Performance Impact For Logging

• Heisenberg’s uncertainty principle

• Setting log level to INFO still has a performance impact

• Since Logger determines whether to log the message the parameter must be evaluated even when not used

22

logger.finest(getSomeStatusData());

Always executed

Page 22: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Supplier<T>

• Represents a supplier of results

• All relevant logging methods now have a version that takes a Supplier

• Pass a description of how to create the log message

– Not the message

• If the Logger doesn’t need the value it doesn’t invoke the Lambda

• Can be used for other conditional activities

23

logger.finest(() -> getSomeStatusData());

Page 23: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Avoiding Loops In Streams

Page 24: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Functional v. Imperative

• For functional programming you should not modify state

• Java supports closures over values, not closures over variables

• But state is really useful…

25

Page 25: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Counting Methods That Return Streams

26

Still Thinking Impaeratively Way

Set<String> sourceKeySet = streamReturningMethodMap.keySet();

LongAdder sourceCount = new LongAdder();

sourceKeySet.stream().forEach(c ->

sourceCount.add(streamReturningMethodMap.get(c).size()));

Page 26: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Counting Methods That Return Streams

27

Functional Way

sourceKeySet.stream().mapToInt(c -> streamReturningMethodMap.get(c).size()).sum();

Page 27: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Printing And Counting Functional Interfaces

28

Still Thinking Imperatively Way

LongAdder newMethodCount = new LongAdder();

functionalParameterMethodMap.get(c).stream().forEach(m -> {

output.println(m);

if (isNewMethod(c, m)) newMethodCount.increment();

});

return newMethodCount.intValue();

Page 28: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Printing And Counting Functional Interfaces

29

More Functional, But Not Pure Functional

int count = functionalParameterMethodMap.get(c).stream().mapToInt(m -> {

int newMethod = 0;output.println(m);

if (isNewMethod(c, m)) newMethod = 1;

return newMethod}).sum();

There is still state being modified in the Lambda

Page 29: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Printing And Counting Functional Interfaces

30

Even More Functional, But Still Not Pure Functional

int count = functionalParameterMethodMap.get(nameOfClass).stream().peek(method -> output.println(method)).mapToInt(m -> isNewMethod(nameOfClass, m) ? 1 : 0) .sum();

Strictly speaking printing is a side effect, which is not purely functional

Page 30: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

The Art Of Reduction(Or The Need to Think Differently)

Page 31: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

A Simple Problem

• Find the length of the longest line in a file

• Hint: BufferedReader has a new method, lines(), that returns a Stream

32

BufferedReader reader = ...

reader.lines().mapToInt(String::length).max().getAsInt();

Page 32: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Another Simple Problem

• Find the length of the longest line in a file

33

Page 33: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Naïve Stream Solution

• That works, so job done, right?

• Not really. Big files will take a long time and a lot of resources

• Must be a better approach

34

String longest = reader.lines().sort((x, y) -> y.length() - x.length()).findFirst().get();

Page 34: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

External Iteration Solution

• Simple, but inherently serial

• Not thread safe due to mutable state

35

String longest = "";

while ((String s = reader.readLine()) != null)if (s.length() > longest.length())

longest = s;

Page 35: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Recursive Approach: The Method

36

String findLongestString(String s, int index, List<String> l) {if (index == l.size() - 1) {if (s.length() > l.get(index).length())

return s;return l.get(index);

}

String s2 = findLongestString(l.get(start), index + 1, l);

if (s.length() > s2.length())return s;

return s2;}

Page 36: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Recursive Approach: Solving The Problem

• No explicit loop, no mutable state, we’re all good now, right?

• Unfortunately not - larger data sets will generate an OOM exception

37

List<String> lines = new ArrayList<>();

while ((String s = reader.readLine()) != null)lines.add(s);

String longest = findLongestString("", 0, lines);

Page 37: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

A Better Stream Solution

• The Stream API uses the well known filter-map-reduce pattern

• For this problem we do not need to filter or map, just reduce

Optional<T> reduce(BinaryOperator<T> accumulator)

• BinaryOperator is a subclass of BiFunction, but all types are the same

• R apply(T t, U u) or T apply(T x, T y)

38

Page 38: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

A Better Stream Solution

• The key is to find the right accumulator

– The accumulator takes a partial result and the next element, and returns a new partial result

– In essence it does the same as our recursive solution

– But back to front

– And without all the stack frames or List overhead

39

Page 39: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

A Better Stream Solution

• Use the recursive approach as an accululator for a reduction

40

String longestLine = reader.lines().reduce((x, y) -> {

if (x.length() > y.length())return x;

return y;}).get();

Page 40: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

A Better Stream Solution

• Use the recursive approach as an accululator for a reduction

41

String longestLine = reader.lines().reduce((x, y) -> {

if (x.length() > y.length())return x;

return y;}).get();

x in effect maintains state for us, by providing the partial result, which is the longest string found so far

Page 41: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

The Simplest Stream Solution

• Use a specialised form of max()

• One that takes a Comparator as a parameter

• comparingInt() is a static method on Comparator– Comparator<T> comparingInt(ToIntFunction<? extends T> keyExtractor)

42

reader.lines().max(comparingInt(String::length)).get();

Page 42: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Conclusions

Page 43: Lambdas : Beyond The Basics

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.

Conclusions

• Lambdas provide a simple way to parameterise behaviour

• The Stream API provides a functional style of programming

• Very powerful combination

• Does require developers to think differently

• Avoid loops, even non-obvious ones!

44

Page 44: Lambdas : Beyond The Basics

Simon RitterOracle Corporartion

Twitter: @speakjava

Copyright © 2014, Oracle and/or its affiliates. All rights reserved.