Download 08 - Lambda Expressions

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Lambda Expressions
In the technical keynote address for JavaOne 2013,
Mark Reinhold, chief architect for the Java Platform
Group at Oracle, described lambda expressions as the
single largest upgrade to the Java programming model
ever.
©SoftMoore Consulting
Slide 1
Java 8 Summary
• Lambda expressions, functional interfaces, and streams
• New date/time API
• Interfaces − can now contain static and default methods
• Nashorn JavaScript Engine (replaces Rhino)
• Other API additions/enhancements
–
–
–
–
–
–
JavaFX (Swing now in maintenance mode)
Concurrency
Collections
I/O
Security
…
©SoftMoore Consulting
Slide 2
Lambda Expressions
• The major new features being added to Java 8 center
around a functional programming construct known as a
lambda expression.
•
Lambda expressions simplify the implementation of
certain types of programming problems.
•
For example, many applications in mathematics require
that a function (technically a pointer or reference to a
function) be passed as a parameter.
– easy to do in most other programming languages
– cumbersome in Java prior to version 8
©SoftMoore Consulting
Slide 3
A Motivating Example from Mathematics
• Simpson’s Rule is a numerical
technique to approximate

a definite integral; e.g.,  sin( x )dx
0
•
(Answer = 2; good test case.)
Simpson’s Rule is an algorithm that computes an integral
based on four parameters as follows:
– A function that we want to integrate.
– Two real numbers a and b that represent the endpoints of an
interval [a,b] on the real number line. (Note that the function
should be continuous on this interval.)
– An even integer n that specifies a number of subintervals. In
implementing Simpson's Rule we divide the interval [a,b] into n
subintervals.
©SoftMoore Consulting
Slide 4
Simpson’s Rule in C++
C++ header file for Simpson's Rule (simpson.h)
#if !defined(SIMPSON_H)
#define SIMPSON_H
#include <stdexcept>
using namespace std;
typedef double DoubleFunction(double x);
double integrate(DoubleFunction f, double a, double b, int n)
throw(invalid_argument);
#endif
©SoftMoore Consulting
Slide 5
Calling integrate() in C++
• Suppose that you want to test your implementation of
Simpson’s Rule by approximating the integral of the sine
function from 0 to π (PI) using 30 subintervals.
•
Calling integrate is straightforward in C++.
double result = integrate(sin, 0, M_PI, 30);
In C++ you pass the sine function as easily
as you pass the other three parameters.
©SoftMoore Consulting
Slide 6
Simpson’s Rule in Java
• Java interface for the function parameter
public interface DoubleFunction
{
public double f(double x);
}
• Java signature for method integrate in class Simpson
public static double integrate
(DoubleFunction f, double a, double b, int n)
•
The above is independent of whether or not we will use
lambda expressions. The primary difference with lambda
expressions is in how we pass parameters (more
specifically, how we pass the function parameter) in a
call to method integrate.
©SoftMoore Consulting
Slide 7
Java Without Lambda Expressions
(Version 1: Adapter Class)
• Create an adapter class that implements the
DoubleFunction interface and wraps the call to
Math.sin().
import com.softmoore.math.DoubleFunction;
public class DoubleFunctionSineAdapter implements DoubleFunction
{
public double f(double x)
{
return Math.sin(x);
}
}
•
Use the adapter class to call method integrate()
DoubleFunctionSineAdapter sine = new DoubleFunctionSineAdapter();
double result = Simpson.integrate(sine, 0, Math.PI, 30);
©SoftMoore Consulting
Slide 8
Comments on Version 1
Compare the call to integrate() in C++ versus what was
required to call integrate() in earlier versions of Java.
•
With C++, we simply called integrate() passing in the
four parameters.
•
With Java, we had to create a new adapter class and then
instantiate this class in order to make the call. If we
wanted to integrate several functions, we would need to
write adapter classes for each of them.
©SoftMoore Consulting
Slide 9
Java Without Lambda Expressions
(Version 2: Anonymous Class)
DoubleFunction sineAdapter = new DoubleFunction()
{
public double f(double x)
{
return Math.sin(x);
}
};
double result = Simpson.integrate(sineAdapter, 0, Math.PI, 30);
Not much better.
©SoftMoore Consulting
Slide 10
Lambda Expressions
• A lambda expression is an annomous method; i.e., a
block of code with parameters.
•
Lambda expressions
– are also known as closures, function literals, or simply lambdas
– are formally defined by Java Specification Request (JSR) 335
•
Motivation: to simplify the ability to create and use
objects that are essentially functions.
– provide better support for functional programming
– provide a light-weight alternative to adapter classes and
anonymous classes for functional interfaces
©SoftMoore Consulting
Slide 11
Functional Interfaces and
Lambda Expressions
• A functional interface is an interface with a single
abstract method.
– an interface that requires implementation of only a single method
in order to satisfy its requirements
– Note: As of Java 8, interfaces can contain static and default
methods. These are not abstract.
•
Conversion to a functional interface is the only thing you
can do with a lambda expression in Java.
Note that DoubleFunction is a functional interface.
©SoftMoore Consulting
Slide 12
Examples of Functional Interfaces in Java
public interface Comparator<T>
{
int compare(T o1, T o2);
...
// other default and static methods added in Java 8
}
public interface ActionListener extends EventListener
{
public void actionPerformed(ActionEvent e);
}
public interface Runnable
{
public void run();
}
©SoftMoore Consulting
Slide 13
Annotating Functional Interfaces
• Java 8 has a new annotation, @FunctionalInterface,
that can be used for functional interfaces.
•
The annotation is not required, but if used,
– it provides an extra check that everything is consistent
(similar to the @Override annotation in earlier versions of Java)
– the javadoc page for the interface includes a statement that the
interface is a functional interface.
©SoftMoore Consulting
Slide 14
Syntax of Lambda Expressions
Three parts
•
A list of parameters enclosed in parentheses
– Parameter types can be explicitly declared, or they can be
inferred from the context.
– Empty parentheses indicate that there are no parameters.
– For a single parameter whose type is inferred, parentheses may
be omitted
•
The lambda operator
– arrow token ->
•
A function body, which can be either of the following:
– a statement block (enclosed in braces)
– a single expression (return type is that of the expression)
©SoftMoore Consulting
Slide 15
Examples of Lambda Expressions
(int x, int y) -> x + y
// returns the sum of two integers
(x, y) -> x + y
// returns the sum of two numbers (types are inferred)
n -> n % 2 == 0
// returns true if n is even (type is inferred)
() -> 42
// constant function, no parameters
// returns the answer to the ultimate question of
// life, the universe, and everything
©SoftMoore Consulting
Slide 16
Using a Lambda Expression
(Version 1)
DoubleFunction sine = (double x) -> Math.sin(x);
double result = Simpson.integrate(sine, 0, Math.PI, 30);
Note that we did not have to write the adapter class
or create an instance of an anonymous class.
©SoftMoore Consulting
Slide 17
Using a Lambda Expression
(Version 2)
• The name of the functional interface is not part of the
lambda expression but can be inferred based on the
context.
•
The type double for the parameter of the lambda
expression can also be inferred from the context.
•
If there is only one parameter in the lambda expression,
the parentheses can be omitted.
• We can substitute the lambda expression inline as part
of the call.
double result
= Simpson.integrate(x -> Math.sin(x), 0, Math.PI, 30);
©SoftMoore Consulting
Slide 18
Using a Lambda Expression
(Version 3)
• Another related feature in Java 8 is something called a
method reference, which allows us to refer to an existing
method by name.
•
Method references can be used in place of lambda
expressions as long as they satisfy the requirements of
the functional interface.
•
For static methods the syntax is
Classname::methodName.
double result
= Simpson.integrate(Math::sin, 0, Math.PI, 30);
Compare with C++ version.
©SoftMoore Consulting
Slide 19
The March of Progress
(Cay Horstmann)
• 1980: C
printf("%10.2f", x);
•
1988: C++
cout << setw(10) << setprecision(2) << showpoint << x;
• 1996: Java
NumberFormat formatter = NumberFormat.getNumberInstance();
formatter.setMinimumFractionDigits(2);
formatter.setMaximumFractionDigits(2);
String s = formatter.format(x);
for (int i = s.length(); i < 10; i++) System.out.print(' ');
System.out.print(s);
•
2004: Java
System.out.printf("%10.2f", x);
©SoftMoore Consulting
Slide 20
Another Example
• Suppose that you have an array of strings that you want
to sort in two different ways
– by their natural (lexicographic or alphabetic) ordering
– by their length (shortest strings before longest)
String[] words = ... ;
•
Consider two sorting methods in class Arrays
static void sort(Object[] a)
// sorts according to the natural ordering of its elements
static <T> void sort(T[] a, Comparator<? super T> c)
// sorts according to the specified comparator
©SoftMoore Consulting
Slide 21
Another Example
(continued)
• Calling
Arrays.sort(words);
will sort the array according to the natural ordering of
type String.
• To sort the array by the length of the strings, we can use
a lambda expression.
Arrays.sort(words,
(word1, word2) -> word1.length – word2.length());
Question: What about this for the Lambda expression?
Integer.compare(word1.length(), word2.length())
Subtraction o.k. for string lengths but not for comparing integers
in general − can overflow for large operands with opposite signs.
©SoftMoore Consulting
Slide 22
Iterators in Java
• Version 1 (Enumerations in very early versions of Java)
Enumeration e = names.elements();
while (e.hasMoreElements())
{
String name = (String) e.nextElement();
System.out.println(name);
}
•
Version 2 (Iterators since Java version 1.2)
Iterator i = names.iterator();
while (i.hasNext())
{
String name = (String) i.next();
System.out.println(name);
}
©SoftMoore Consulting
Slide 23
Iterators in Java
(continued)
• Version 3 (Enhanced for statement since Java 5)
for (String name : names)
System.out.println(name);
•
Version 4 (New forEach() iterator in Java 8)
names.forEach(name -> System.out.println(name));
Note change from external (a.k.a. active) iterators
to internal (a.k.a. passive) iterators.
See also the new Stream API for collections.
− transformations (e.g., sorted, distinct, …)
− filters (subcollection based on predicate)
− reduction (max, count, …)
− parallel versions
©SoftMoore Consulting
Slide 24
Streams
•
A stream is an abstraction for a sequence of elements
supporting sequential and parallel aggregate operations.
– not a data structure
– can be infinite
•
A stream pipeline consists of three types of things
– a source
– zero or more intermediate operations
– a terminal operation (yields a result or a side-effect
Source
stream Intermediate stream Intermediate stream
Operation
Operation
©SoftMoore Consulting
Terminal
Operation
Result
Slide 25
Stream Example
List<String> names = new LinkedList<>();
...
long count = names.stream()
.filter(name -> name.startsWith("A"))
.count();
©SoftMoore Consulting
Slide 26
Parallel Streams
• Collection classes support both sequential and parallel
stream
– method stream() returns a sequential stream
– method parallelStream() returns a parallel stream
• Parallel streams allow pipeline operations to be
executed concurrently in separate Java threads
– potential performance improvement but order in which collection
elements are processed can change
• Parallel streams are implemented using the Fork/Join
framework that was added to Java 7.
©SoftMoore Consulting
Slide 27
Parallel Streams
(continued)
• Parallelizing the operations in a stream pipeline is as
simple as replacing the call to stream() with a call to
parallelStream().
•
Don’t assume that a parallel stream will return a result
faster than a sequential stream
– many factors affect performance
– some overhead involved with using multiple threads
– will likely result in slower performance with linked data structures
©SoftMoore Consulting
Slide 28
References
• Java SE 8 for the Really Impatient, Cay Horstmann, Addison
Wesley, 2014.
http://www.informit.com/store/java-se-8-for-the-really-impatient-9780321927767
• Java Tutorial (new sections on lambda expressions)
http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
https://docs.oracle.com/javase/tutorial/collections/streams/
• “Java programming with lambda expressions,” John I. Moore, Jr.,
JavaWorld.
http://www.javaworld.com/article/2092260/java-se/
java-programming-with-lambda-expressions.html
• “Iterating over collections in Java 8,” John I. Moore, Jr., JavaWorld.
http://www.javaworld.com/article/2461744/java-language/java-language-iteratingover-collections-in-java-8.html
• State of the Lambda (Brian Goetz)
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
©SoftMoore Consulting
Slide 29