Upgrade to Pro — share decks privately, control downloads, hide ads and more …

test

Avatar for frostman frostman
December 07, 2011
50

 test

Avatar for frostman

frostman

December 07, 2011
Tweet

Transcript

  1. <Insert Picture Here> Language / Library / VM co-evolution in

    Java SE 8 Brian Goetz Java Language Architect, Oracle Corporation Thursday, November 17, 2011
  2. 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. Thursday, November 17, 2011
  3. Agenda • Background • Lambda Expressions • Library Evolution •

    Virtual Extension Methods • Evolving Collections • Wrap-up Thursday, November 17, 2011
  4. Where are we? • Multicore hardware is now the default

    • Moore’s law delivering more cores, not faster cores • We must learn to write software that parallelizes gracefully • All components of the Java SE platform – language, libraries, and VM – are co-evolving to meet the challenge Thursday, November 17, 2011
  5. Where are we going? • Goal: easy-to-use parallel libraries for

    Java • Libraries can hide a host of complex concerns (task scheduling, thread management, load balancing) • Goal: reduce conceptual and syntactic gap between serial and parallel expressions of the same computation • Right now, the serial code and the parallel code for a given computation don’t look anything like each other • Fork-join (added in Java SE 7) is a good start, but not enough Thursday, November 17, 2011
  6. It’s all about the libraries • Most of the time,

    we should prefer to evolve the programming model through libraries • Time to market – can evolve libraries faster than language • Decentralized – more library developers than language developers • Risk – easier to change libraries, more practical to experiment • Impact – language changes require coordinated changes to multiple compilers, IDEs, and other tools • But sometimes we reach the limits of what is practical to express in libraries, and need some help from the language Thursday, November 17, 2011
  7. The problem: external iteration List<Student> students = ... double highestScore

    = 0.0; for (Student s : students) { if (s.gradYear == 2011) { if (s.score > highestScore) { highestScore = s.score; } } } • Client controls iteration • Inherently serial: iterate from beginning to end • Not thread-safe because business logic is stateful (mutable accumulator variable) Thursday, November 17, 2011
  8. Internal iteration with inner classes SomeCoolList<Student> students = ... double

    highestScore = students.filter(new Predicate<Student>() { public boolean op(Student s) { return s.getGradYear() == 2011; } }).map(new Mapper<Student,Double>() { public Double extract(Student s) { return s.getScore(); } }).max(); • Iteration, filtering and accumulation are handled by the library • Not inherently serial – traversal may be done in parallel • Traversal may be done lazily, so one pass rather than three • Thread-safe because business logic is stateless in the client • But…high barrier to use • Syntactically ugly • Have to switch libraries Thursday, November 17, 2011
  9. Internal iteration with lambdas SomeCoolList<Student> students = ... double highestScore

    = students.filter(Student s -> s.getGradYear() == 2011) .map(Student s -> s.getScore()) .max(); • More readable • More abstract • Less error-prone • No reliance on mutable state • Easier to make parallel • How are we going to get there? Thursday, November 17, 2011
  10. Why closures for Java? • Provide libraries a path to

    multicore • Today, developer’s primary tool for computing over aggregates is the for loop – which is fundamentally serial • Parallel-friendly APIs need internal iteration • Internal iteration needs a concise code-as-data mechanism • Empower library developers • Closures are useful for all kinds of libraries, serial or parallel • Enable a higher degree of cooperation between libraries and client code • It’s about time! • Java is the lone holdout among mainstream OO languages at this point to not have closures • Adding closures to Java is no longer a radical idea Thursday, November 17, 2011
  11. The real challenge: library evolution • Adding closures is a

    big language change • If Java had closures from day 1, our APIs would definitely look different • So adding closures now makes our aging APIs show their age even more • Most important APIs (Collections) are based on interfaces • Can’t add to interfaces without breaking source compatibility • Adding closures to Java, but not upgrading the APIs to use them, would be silly • Therefore we also need better mechanisms for library evolution • Lots of options here – each with pros and cons Thursday, November 17, 2011
  12. Lambda Expressions • Lambda expressions are anonymous functions • Like

    a method, has a typed argument list, a return type, a set of thrown exceptions, and a body double highestScore = students.filter(Student s -> s.getGradYear() == 2011) .map(Student s -> s.getScore()) .max(); Thursday, November 17, 2011
  13. What is the type of a lambda expression? • For

    years, we’ve used single-method interfaces to represent functions and callbacks • Define: a functional interface is an interface with one method • Functional interfaces are identified structurally • The type of a lambda expression will be a functional interface interface Comparator<T> { boolean compare(T x, T y); } interface FileFilter { boolean accept(File x); } interface DirectoryStream.Filter<T> { boolean accept(T x); } interface Runnable { void run(); } interface ActionListener { void actionPerformed(…); } interface Callable<T> { T call(); } Thursday, November 17, 2011
  14. Target typing • A lambda expression is a way to

    create an instance of a functional interface • Which functional interface? Infer from context! • Works both in assignment and method invocation contexts • Can use casts if needed to resolve ambiguity Comparator<String> c = new Comparator<String>() { public int compare(String x, String y) { return x.length() - y.length(); } }; Comparator<String> c = (String x, String y) -> x.length() - y.length(); Thursday, November 17, 2011
  15. Local variable capture • Lambda expressions can refer to effectively

    final local variables from the enclosing scope • Effectively final means that the variable meets the requirements for final variables (e.g., assigned once), even if not explicitly declared final • This is a form of type inference void expire(File root, long before) { ... root.listFiles(File p -> p.lastModified() <= before); ... } Thursday, November 17, 2011
  16. Lexical scoping • The meaning of names are the same

    inside the lambda as outside • Including ‘this’ – refers to the enclosing object, not the lambda itself • Think of ‘this’ as a final predefined local class SessionManager { long before = ...; void expire(File root) { ... // refers to ‘this.before’, just like outside the lambda root.listFiles(File p -> checkExpiry(p.lastModified(), before)); } boolean checkExpiry(long time, long expiry) { ... } } Thursday, November 17, 2011
  17. Improved Type Inference • Compiler can often infer parameter types

    in a lambda expression • Parameter types in the lambda expression are inferred based on the target functional interface’s method signature • Fully statically typed – no dynamic typing here • Type inference is “more typing with less typing” • First appeared in Java 5.0 – generic methods • Extended in Diamond feature from Project Coin • Further extended in Project Lambda Collections.sort(ls, (String x, String y) -> x.length() - y.length()); Collections.sort(ls, (x, y) -> x.length() - y.length()); Thursday, November 17, 2011
  18. Method References • Method references let us reuse a method

    as a lambda expression FileFilter x = new FileFilter() { public boolean accept(File f) { return f.canRead(); } }; Thursday, November 17, 2011
  19. Method References • Method references let us reuse a method

    as a lambda expression FileFilter x = (File f) -> f.canRead(); FileFilter x = new FileFilter() { public boolean accept(File f) { return f.canRead(); } }; Thursday, November 17, 2011
  20. Method References • Method references let us reuse a method

    as a lambda expression FileFilter x = (File f) -> f.canRead(); FileFilter x = File::canRead; FileFilter x = new FileFilter() { public boolean accept(File f) { return f.canRead(); } }; Thursday, November 17, 2011
  21. Putting it all together With a little help from the

    libraries • With some minor library improvements, we can make common idioms more expressive, reliable, and compact Collections.sort(people, new Comparator<Person>() { public int compare(Person x, Person y) { return x.getLastName().compareTo(y.getLastName()); } }); Thursday, November 17, 2011
  22. Putting it all together With a little help from the

    libraries • With some minor library improvements, we can make common idioms more expressive, reliable, and compact Collections.sort(people, new Comparator<Person>() { public int compare(Person x, Person y) { return x.getLastName().compareTo(y.getLastName()); } }); Collections.sort(people, (Person x, Person y) -> x.getLastName().compareTo(y.getLastName()) }); Thursday, November 17, 2011
  23. Putting it all together With a little help from the

    libraries • With some minor library improvements, we can make common idioms more expressive, reliable, and compact Collections.sort(people, new Comparator<Person>() { public int compare(Person x, Person y) { return x.getLastName().compareTo(y.getLastName()); } }); Collections.sort(people, comparing(Person p -> p.getLastName()) ); Thursday, November 17, 2011
  24. Putting it all together With a little help from the

    libraries • With some minor library improvements, we can make common idioms more expressive, reliable, and compact Collections.sort(people, new Comparator<Person>() { public int compare(Person x, Person y) { return x.getLastName().compareTo(y.getLastName()); } }); Collections.sort(people, comparing(p -> p.getLastName())); Thursday, November 17, 2011
  25. Putting it all together With a little help from the

    libraries • With some minor library improvements, we can make common idioms more expressive, reliable, and compact Collections.sort(people, new Comparator<Person>() { public int compare(Person x, Person y) { return x.getLastName().compareTo(y.getLastName()); } }); Collections.sort(people, comparing(Person::getLastName)); Thursday, November 17, 2011
  26. Putting it all together With a little help from the

    libraries • With some minor library improvements, we can make common idioms more expressive, reliable, and compact Collections.sort(people, new Comparator<Person>() { public int compare(Person x, Person y) { return x.getLastName().compareTo(y.getLastName()); } }); people.sort(comparing(Person::getLastName)); Thursday, November 17, 2011
  27. Putting it all together With a little help from the

    libraries • With some minor library improvements, we can make common idioms more expressive, reliable, and compact Collections.sort(people, new Comparator<Person>() { public int compare(Person x, Person y) { return x.getLastName().compareTo(y.getLastName()); } }); people.sort(comparing(Person::getLastName)); More concise More abstract More reuse More object-oriented Thursday, November 17, 2011
  28. Where are we trying to get to? • What we

    want: aggregate operations on collections • New methods on Collections that allow for bulk operations • Such as: filter, map, reduce, forEach, sort • Which can run in parallel • How are we going to get there? • Can’t add new methods to interfaces without modifying all implementations • Can’t necessarily find or control all implementations int heaviestBlueBlock = blocks.filter(b -> b.getColor() == BLUE) .map(Block::getWeight) .reduce(0, Integer::max); Thursday, November 17, 2011
  29. API evolution is a first-class problem • Interfaces are a

    double-edged sword • Once published, cannot add to them without breaking existing implementations • Fundamental problem: can’t evolve interface-based APIs • The older an API gets, the more obvious the decay • We’re a victim of our own success; Java has lots of old APIs • Lots of bad choices here • Let the API stagnate • Try and replace it in entirety – every few years! • Nail bags on the side (e.g., Collections.sort()) • Key Principle: burden of API evolution should fall to implementors, not users • Solutions that require users to permanently cruft up their code to use new features are undesirable Thursday, November 17, 2011
  30. Library-based evolution options 1. Status quo: add more static methods

    to Collections helper class • Not very object-oriented 2. Don’t evolve existing collections, but add some new parallel implementation classes • Doesn’t do anything for serial programming model • Far removed from existing collections 3. Build “Collections 2” • Unrealistic – the Collections types permeate the library • Huge design space, still stuck with VM limitations (32-bit arrays, erasure) 4. Add aggregate operations to concrete classes • Hurts everyone who follows best-practice style advice • Code riddled with casts • Ties users to implementation instead of interface contracts 5. Add new parent interfaces (SuperList) with aggregate ops • More casts and conversions • Where does this lead to? NewSuperImprovedList3? Thursday, November 17, 2011
  31. Language-based evolution options 1. Static (C#-style) extension methods • Proven,

    but limited 2. Interface versioning • Unproven, conflicts with existing modularity design 3. Expanders, Classboxes • Unproven 4. Traits • Might be a nice idea if we were starting over 5. Virtual extension methods Thursday, November 17, 2011
  32. Static extension methods in C# • C# has static extension

    methods • Makes a static method call look like an instance call • Rewrites calls at compile time from list.sort(args) to Collections.sort(list, args) • Simple to implement, no VM changes • Limitations • Classes don’t know about their extension methods • So cannot provide a “better” implementation • Not reflectively discoverable • No covariant overrides • Brittle – if default changes, clients have to be recompiled • Poor interaction with existing instance methods of same name • Not very object-oriented Thursday, November 17, 2011
  33. Solution: virtual extension methods • Virtual extension methods are specified

    in the interface • The sort method below is an extension method • From caller’s perspective, an ordinary interface method • List provides a default implementation • Default is only used when implementation classes do not provide a body for the extension method • Implementation classes can provide a better version, or not • “If you cannot afford an implementation of forEach, one will be provided for you at no charge.” • Drawback: requires VM support interface List<T> { // existing methods, plus void sort(Comparator<? super T> cmp) default { Collections.sort(this, cmp); }; } Thursday, November 17, 2011
  34. Virtual extension methods • Gack, is this multiple inheritance in

    Java? • Yes, but Java already has multiple inheritance of types • This adds multiple inheritance of behavior too • But not state, which is where most of the trouble is • Though can still be a source of complexity due to separate compilation and dynamic linking • API evolution may be the primary motivator, but useful as an inheritance mechanism in itself • Most of the way there to stateless traits…shhhh Thursday, November 17, 2011
  35. Method inheritance from interfaces • Since a class can now

    inherit behavior from multiple supertypes, how do we choose? • Rule #1: treat inheritance of behavior from classes and interfaces separately • If there is an inheritance conflict between a superclass and a superinterface, the superclass takes priority • Defaults only come into play if there is no declaration for that method in the superclass chain • Rule #2: Conflicts between multiple interfaces are resolved using subtyping • More specific interfaces take priority • Conflicts can also be resolved explicitly • Invocation is resolved to a default if there is a unique, most-specific default-providing interface • Otherwise, user can implement method explicitly Thursday, November 17, 2011
  36. Method resolution Pruning less specific interfaces • If interface B

    extends A, then B is more specific than A • If both A and B provide a default, we remove A from consideration because B is more specific • The fact that C<T> declares Collection<T> as an immediate supertype is irrelevant • Set is more specific and also provides a default, so it wins over Collection interface Collection<T> { public Collection<T> filter(Predicate<T> p) default …; } interface Set<T> extends Collection<T> { public Set<T> filter(Predicate<T> p) default …; } class D<T> implements Set<T> { ... } class C<T> extends D<T> implements Collection<T> { … } Thursday, November 17, 2011
  37. Method resolution Handling diamond-shaped inheritance • Diamonds do not pose

    a problem • When analyzing D, there is a unique, most specific default-providing interface – A • Therefore D inherits its implementation of m() from A • Diamonds are a problem for state inheritance, not behavior interface A { void m() default ...; } interface B extends A { } interface C extends A { } class D implements B, C { ... } Thursday, November 17, 2011
  38. Method resolution Explicit disambiguation • If ambiguous multiple inheritance is

    detected at compile-time, the programmer must provide a non- abstract method • New syntax X.super.m() to invoke the default inherited from immediate superinterface X interface A { void m() default ...; } interface B { void m() default ...; } class C implements A, B { /* required */ public void m() { A.super.m(); } } Thursday, November 17, 2011
  39. Extension methods are a VM feature • Might be possible

    to implement extension methods entirely as a language feature, but this would create many downstream problems • Brittleness • Binary compatibility • In reality, everything else about inheritance is a VM feature • Trying to implement otherwise would cause visible seams • Built into behavior of invocation bytecodes • Bonus: available to other JVM languages too • VM evolving to meet the needs of the language Thursday, November 17, 2011
  40. Example: optional methods • Virtual extension methods can reduce the

    implementation burden (and boilerplate) • Most implementations of Iterator don’t provide a useful remove() method • So why make the developer declare it? interface Iterator<T> { boolean hasNext(); T next(); void remove() default { throw new UnsupportedOperationException(); }; } Thursday, November 17, 2011
  41. Example: retrofits • JDK 1.0 had Enumeration; later replaced with

    Iterator • APIs that return Enumeration became second-class citizens • So let’s retrofit Enumeration to implement Iterator interface Enumeration<E> extends Iterator<E> { boolean hasMoreElements(); E nextElement(); boolean hasNext() default { return hasMoreElements(); } E next() default { return nextElement(); } void remove() default { throw new UnsupportedOperationException(); } } Thursday, November 17, 2011
  42. Example: simple extensions interface Collection<E> { removeAll(Predicate<? super E> p)

    default { ... }; } interface List<E> { void sort(Comparator<? super E> c) default { ... }; } interface Reader { void eachLine(Block<String> block) default { ... }; } collection.removeAll(s -> s.length() > 20); collection.removeAll(String::isEmpty); list.sort(comparing(Person::getLastName).reverse()); reader.eachLine(s -> { System.out.println(s); }); Thursday, November 17, 2011
  43. Extension methods are virtual interface List<E> { void sort(Comparator<? super

    E> c) default Collections.sort; void parallelSort(Comparator<? super E> c) default Collections.sort; // can’t do much better } class ArrayList<E> implements List<E> { public void parallelSort(Comparator<? super E> c) { ForkJoinUtils.parallelSort(myArray, offset, len); } } Thursday, November 17, 2011
  44. Compatibility goals • The whole point of extension methods is

    being able to compatibly evolve APIs • Motivated by the fact that we’ve got some APIs in serious need of evolution • Compatibility has multiple faces • Source compatibility • Binary compatibility • The key operation we care about is adding new methods with defaults to existing interfaces • Without necessarily recompiling the implementation class • Also care about adding defaults to existing methods, and changing defaults on existing extension methods • Removals of most kinds are unlikely to be compatible Thursday, November 17, 2011
  45. Remember what we wanted to write? • Need to add

    new methods to Collection / List • Lots of design choices here • Eager vs lazy • In-place vs create-new • Serial vs parallel • Where in the hierarchy to put new methods List<Student> students = ... double highestScore = students.filter(s -> s.getGradYear() == 2011) .map(s -> s.getScore()) .reduce(0.0, Integer::max); Thursday, November 17, 2011
  46. Attractive target: Iterable • Add streaming operations to Iterable •

    All collections get these for free • Default easily implemented in terms of iterator() • Similar to Scala’s Traversable, CLR’s IReadOnlyList public interface Iterable<T> { Iterator<T> iterator(); boolean isEmpty() default ...; void forEach(Block<? super T> block) default ...; Iterable<T> filter(Predicate<? super T> predicate) default ...; <U> Iterable<U> map(Mapper<? super T, ? extends U> mapper) default ...; T reduce(T base, Operator<T> reducer) default ...; Iterable<T> sorted(Comparator<? super T> comp) default ...; <C extends Collection<? super T>> C into(C collection) default ...; // and more... } Thursday, November 17, 2011
  47. Eager vs lazy • New methods are a mix of

    lazy and eager • Naturally lazy operations: filter, map, cumulate • Naturally eager operations: reduce, forEach, into • Many useful operations can be represented as pipelines of source-lazy-lazy-eager • collection-filter-map-reduce • array-map-sorted-foreach • The laziness is mostly invisible • No new abstractions for LazyCollection, LazyList, etc Thursday, November 17, 2011
  48. Going parallel • A collection knows how to iterate itself

    • By adding to Iterable, all Collections types now expose bulk data operations • Without changing the Collections implementations! • Individual classes can provide better implementations • Can add direct-access versions to ArrayList • Or, create new RandomAccessList as new parent of ArrayList, and put extensions there • Can we do the same for parallel algorithms? • What is the parallel equivalent of Iterable? Thursday, November 17, 2011
  49. Going parallel • Many problems yield to “divide-and-conquer” recursive decomposition

    • Break down problem into subproblems, solve in parallel, and combine results • Subproblems broken down recursively until small enough for solution by sequential algorithm • JDK 7 provides general-purpose Fork/Join framework based on work stealing • Fork-join parallelism is powerful and efficient • But, want to protect users from having to actually write fork- join code Thursday, November 17, 2011
  50. Recursive decomposition • Recursive decomposition + work stealing offer a

    way to write portable performant parallel algorithms • Can parallelize many sequential algorithms this way Result solve(Problem p) { if (p.size() < SMALL_ENOUGH) return p.solveSequentially(); else { Problem left = p.extractLeftHalf(); Problem right = p.extractRightHalf(); IN_PARALLEL { solve(left); solve(right); } return combine(left, right); } } Thursday, November 17, 2011
  51. Work stealing • Fork-join is implemented using work-stealing • Create

    a pool of worker threads • Each worker thread maintains a private work queue (deque) • When forking, worker push new tasks on queue • When waiting or idle, pop a task off queue and execute it • If worker’s queue is empty, steals work off another’s queue • Result: reasonable load balancing • With no central coordination • With little scheduling overhead • With minimal synchronization costs • Queue access is almost never contended • Stealing infrequent in practice – just enough stealing to distribute work around the pool Thursday, November 17, 2011
  52. Parallel High Score Finder with Fork/ Join class ScoreProblem {

    final List<Student> students; final int size; ScoreProblem(List<Student> ls) { this.students = ls; this.size = this.students.size(); } public double solveSequentially() { double highestScore = 0.0; for (Student s : students) { if (s.gradYear == 2011) { if (s.score > highestScore) { highestScore = s.score; } } } return highestScore; } public ScoreProblem subproblem(int start, int end) { return new ScoreProblem(students.subList(start, end)); } } ForkJoinExecutor pool = new ForkJoinPool(nThreads); ScoreFinder finder = new ScoreFinder(problem); pool.invoke(finder); class ScoreFinder extends RecursiveAction { private final ScoreProblem problem; double highestScore; protected void compute() { if (problem.size < THRESHOLD) highestScore = problem.solveSequentially(); else { int m = problem.size / 2; ScoreFinder left, right; left = new ScoreFinder(problem.subproblem(0, m)) right = new ScoreFinder(problem.subproblem(m, problem.size)); forkJoin(left, right); highestScore = Math.max(left.highestScore, right.highestScore); } } } ForkJoinExecutor pool = new ForkJoinPool(nThreads); ScoreFinder finder = new ScoreFinder(problem); pool.invoke(finder); class ScoreFinder extends RecursiveAction { private final ScoreProblem problem; double highestScore; protected void compute() { if (problem.size < THRESHOLD) highestScore = problem.solveSequentially(); else { int m = problem.size / 2; ScoreFinder left, right; left = new ScoreFinder(problem.subproblem(0, m)) right = new ScoreFinder(problem.subproblem(m, problem.size)); forkJoin(left, right); highestScore = Math.max(left.highestScore, right.highestScore); } } } Thursday, November 17, 2011
  53. Going parallel • Fork/Join relies on the client to decompose

    the problem • Must configure thread pool • Must choose sequential vs. parallel threshold • Must determine decomposition approach (2-way, n-way…) • Fork/Join is effective, but is really “parallelism assembly language” • More powerful (and object-oriented) for the collection to decompose itself! • Iterable embodies internal iteration, so let’s define a mechanism to embody parallel internal iteration Thursday, November 17, 2011
  54. Iterable and Spliterable • The parallel equivalent of Iterable is

    … Spliterable • A Spliterable can be decomposed into two smaller chunks • Small chunks can be iterated sequentially • Most data structures (arrays, trees, maps) admit a natural means of subdividing themselves • Default implementations work in terms of Spliterable • Add parallel() method to collections to dispense a Spliterable public interface Spliterable<T> { Iterator<T> iterator(); Spliterable<T> left(); Spliterable<T> right(); Iterable<T> sequential(); // plus extension methods } Thursday, November 17, 2011
  55. Explicit but unobtrusive parallelism List<Student> students = new ArrayList<>(...); ...

    double highestScore = students.parallel() .filter(s -> s.getGradYear() == 2011) .map(s -> s.getScore()) .reduce(0.0, Integer::max); • More readable • Better abstraction • No reliance on mutable state • Runs in parallel • Implementation fuses three operations into a single parallel pass • Works on any data structure that knows how to subdivide itself Thursday, November 17, 2011
  56. Spliterable • Methods on Spliterable are analogous to Iterable •

    Individual collections can always override default implementation public interface Spliterable<T> { Iterator<T> iterator(); Spliterable<T> left(); Spliterable<T> right(); Iterable<T> sequential(); boolean isEmpty() default ...; void forEach(Block<? super T> block) default ...; Spliterable<T> filter(Predicate<? super T> predicate) default ...; <U> Spliterable<U> map(Mapper<? super T, ? extends U> mapper) default ...; T reduce(T base, Operator<T> reducer) default ...; Spliterable<T> sorted(Comparator<? super T> comp) default ...; <C extends Collection<? super T>> C into(C collection) default ...; // and more... } Thursday, November 17, 2011
  57. Example: a simple query Problem: Given a personal music library,

    get the set of albums for which at least one track is highly-rated class Library { Set<Album> albums; Set<Album> allAlbums() { return albums; } Set<Album> favoriteAlbums() { // TODO } } class Album { String title; List<Track> tracks; } class Track { String title; String artist; int rating; } Thursday, November 17, 2011
  58. Example: a simple query Identifying a favorite album // Set

    ‘hasFavorite’ to ‘true’ if some track in an album ‘a’ is rated >= 4 Album a = …; boolean hasFavorite = false; for (Track t : a.tracks) { if (t.rating >= 4) { hasFavorite = true; break; } } Thursday, November 17, 2011
  59. Example: a simple query Identifying a Favorite Album // Set

    ‘hasFavorite’ to ‘true’ if some track in an album ‘a’ is rated >= 4 Album a = …; boolean hasFavorite = false; for (Track t : a.tracks) { if (t.rating >= 4) { hasFavorite = true; break; } } boolean hasFavorite = a.tracks.anyMatch(t -> (t.rating >= 4)); Thursday, November 17, 2011
  60. Example: a simple query Making a Set of Favorite Albums

    // Initialize ‘favs’ as a set of favorite albums drawn from ‘albums’ Set<Album> favs = new HashSet<>(); for (Album a : albums) { if (a.tracks.anyMatch(t -> (t.rating >= 4))) favs.add(a); } Thursday, November 17, 2011
  61. Example: a simple query Making a Set of Favorite Albums

    // Initialize ‘favs’ as a set of favorite albums drawn from ‘albums’ Set<Album> favs = new HashSet<>(); for (Album a : albums) { if (a.tracks.anyMatch(t -> (t.rating >= 4))) favs.add(a); } Set<Album> favs = albums.filter(a -> a.tracks.anyMatch(t -> (t.rating >= 4))) .into(new HashSet<>()); Thursday, November 17, 2011
  62. Loop-based vs Lambda-based Set<Album> favs = new HashSet<>(); for (Album

    a : albums) { boolean hasFavorite = false; for (Track t : a.tracks) { if (t.rating >= 4) { hasFavorite = true; break; } } if (hasFavorite) favs.add(a); } Thursday, November 17, 2011
  63. Loop-based vs Lambda-based Set<Album> favs = new HashSet<>(); for (Album

    a : albums) { boolean hasFavorite = false; for (Track t : a.tracks) { if (t.rating >= 4) { hasFavorite = true; break; } } if (hasFavorite) favs.add(a); } Set<Album> favs = albums.filter(a -> a.tracks.anyMatch(t -> (t.rating >= 4))) .into(new HashSet<>()); Thursday, November 17, 2011
  64. Loop-based vs Lambda-based Adding parallelism Set<Album> favs = new HashSet<>();

    for (Album a : albums) { boolean hasFavorite = false; for (Track t : a.tracks) { if (t.rating >= 4) { hasFavorite = true; break; } } if (hasFavorite) favs.add(a); } Set<Album> favs = albums.parallel() .filter(a -> a.tracks.anyMatch(t -> (t.rating >= 4))) .into(new ConcurrentHashSet<>()); Thursday, November 17, 2011
  65. Project Lambda / JSR-335 Status • Project Lambda started December

    2009 • Explorations done through OpenJDK • JSR-335 filed November 2010 • Prototype compiler developed in OpenJDK • Current status • EDR draft #1 now public, available at http://www.jcp.org/en/jsr/summary?id=335 • Compiler prototype binaries available at http://jdk8.java.net/lambda/ • No VM support yet for extension method dispatch Thursday, November 17, 2011
  66. Conclusion • Java needs closures for multiple reasons • Closures

    without lambda-fying the libraries would be poor • Replacing all the libraries is a non-starter • Compatibly evolving interface-based APIs has historically been a problem • So we also need a mechanism for interface evolution • Solution: virtual extension methods • Which is both a language and a VM feature • And which is pretty useful for other things too • Java SE 8 evolves the language, libraries, and VM together Thursday, November 17, 2011