Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
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 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. – Parentheses may be omitted for a single parameter whose type can be inferred. • 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) -> { return x + y; } // returns the sum of two integers (x, y) -> x + y // returns the sum of two numbers // types of parameters and return type are inferred // body is a single expression; braces omitted 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 components. – 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