Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Java 8 Workshop

6,317 views

Published on

Java 8 Workshop

Published in: Software
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • http://dbmanagement.info/Tutorials/Java.htm
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Java 8 Workshop

  1. 1. by Mario Fusco mario.fusco@gmail.com twitter: @mariofusco 8 in Action Lambdas, Streams, and functional-style programming
  2. 2. Project Lambda – A Minimal History ➢ 2006 – Gosling: "We will never have lambdas in Java" ➢ 2007 – 3 different proposals for lambdas in Java ➢ 2008 – Reinhold: "We will never have lambdas in Java" ➢ 2009 – Start of project Lambda (JSR 335) public boolean willJavaHaveLambdas() { return currentYear % 2 == 1; }
  3. 3. From Single Method Interfaces … public interface Comparator<T> { int compare(T o1, T o2); Functional Interface } Collections.sort(strings, new Comparator<String>() { public int compare(String s1, String s2) { return s1.compareToIgnoreCase(s2); } }); ➢ Bulky syntax ➢ Confusion surrounding the meaning of names and this ➢ Inability to capture non-final local variables ➢ Inability to abstract over control flow
  4. 4. … To Lambda Expressions Collections.sort(strings,(s1, s2) -> s1.compareToIgnoreCase(s2)); Lambda expression are always converted to instance of a functional interface Comparator<String> c = (s1, s2) -> s1.compareToIgnoreCase(s2); Compiler figures out the types No need of changing the JVM to create a new type for lambda expressions
  5. 5. Common JDK8 @FunctionalInterfaces ➢ Predicate<T> → test a property of the object passed as argument ➢ Consumer<T> → execute an action on the object passed as argument ➢ Function<T, U> → transform a T to a U ➢ BiFunction<T, U, V> → transform a (T, U) to a V ➢ Supplier<T> → provide an instance of a T (such as a factory) ➢ UnaryOperator<T> → a unary operator from T -> T ➢ BinaryOperator<T> → a binary operator from (T, T) -> T Give a look at java.util.function.*
  6. 6. Anatomy of a lambda expression A lambda expression is like an (anonymous) method: it provides a list of formal parameters and a body s -> s.length() (int x, int y) -> x + y () -> 42 (x, y, z) -> { if (x) { return y; } else { return z; } } The formal parameters of a lambda expression may have either inferred or declared types Return is implicit and can be omitted A lambda body is either a single expression or a block
  7. 7. However … … syntax is probably the less important thing about lambda expression … … the really fundamental thing about lambda expression is … … the huge paradigm shift they imply
  8. 8. Why Lambdas? ➢ Behaviors can be passed to a method together with data ➢ API designers can build more powerful, expressive APIs ➢ More room for generalization ➢ Pass behaviors to a method together with normal data ➢ Libraries remain in control of computation ➢ e.g. internal vs. external iteration ➢ More opportunities for optimization ➢ Laziness ➢ Parallelism ➢ Out-of-order execution ➢ More regular and then more readable code ➢ e.g. nested loops vs. pipelined (fluent) operations ➢ Better composability and reusability
  9. 9. Example 1 Your first Lambda
  10. 10. Internal vs External Iteration
  11. 11. Internal vs External Iteration for (Employee e : employees) { e.setSalary(e.getSalary() * 1.03); } ̶ Inherently serial ̶ Client has to manage iteration ̶ Nested loops are poorly readable employees.forEach(e -> e.setSalary(e.getSalary() * 1.03)); Not only a syntactic change! + Library is in control → opportunity for internal optimizations as parallelization, lazy evaluation, out-of-order execution + More what, less how → better readability + Fluent (pipelined) operations → better readability + Client can pass behaviors into the API as data → possibility to abstract and generalize over behavior → more powerful, expressive APIs
  12. 12. Sorting with Lambdas Comparator<Person> byAge = new Comparator<Person>() { public int compare(Person p1, Person p2) { return p1.getAge() – p2.getAge(); } }; Collections.sort(people, byAge); Functional interface Lambda expression Comparator<Person> byAge = (p1, p2) -> p1.getAge() – p2.getAge(); Collections.sort(people, (p1, p2) -> p1.getAge() – p2.getAge());
  13. 13. Can We Do Better? Comparator<Person> byAge = Comparators.comparing(p -> p.getAge()); Comparator<Person> byAge = Comparators.comparing(Person::getAge); Method reference Readability Collections.sort(people, comparing(Person::getAge)); Collections.sort(people, comparing(Person::getAge).reverse()); Collections.sort(people, comparing(Person::getAge) .compose(comparing(Person::getName))); Reusability Composability
  14. 14. Example 2 Passing behavior with Lambda
  15. 15. OOP vs FP OOP makes code understandable by encapsulating moving parts FP makes code understandable by minimizing moving parts - Michael Feathers
  16. 16. The OOP/FP dualism - OOP public class Bird { } public class Cat { private Bird catch; private boolean full; public void capture(Bird bird) { catch = bird; } public void eat() { full = true; catch = null; } } Cat cat = new Cat(); Bird bird = new Bird(); cat.capture(bird); cat.eat(); The story
  17. 17. The OOP/FP dualism - FP public class Bird { } public class Cat { public CatWithCatch capture(Bird bird) { return new CatWithCatch(bird); } } public class CatWithCatch { private final Bird catch; public CatWithCatch(Bird bird) { catch = bird; } public FullCat eat() { return new FullCat(); } } public class FullCat { } BiFunction<Cat, Bird, FullCat> story = ((BiFunction<Cat, Bird, CatWithCatch>)Cat::capture) .compose(CatWithCatch::eat); FullCat fullCat = story.apply( new Cat(), new Bird() ); Immutability Emphasis on verbs instead of names No need to test internal state: correctness enforced by the compiler
  18. 18. Streams - Efficiency with laziness List<Employee> employess = ... employees.stream() .filter(e -> e.getIncome() > 50000) .map(e -> e.getName()) .forEach(System.out::println); Represents a sequence of element from a source Not a data structure: doesn't store elements but compute them on demand Sources can be Collection, array, generating function, I/O .... Encourages a pipelined ( "fluent" ) usage style Operations are divided between intermediate and terminal Lazy in nature: only terminal operations actually trigger a computation
  19. 19. Evolving APIs with default methods Where does that stream() method come from? public interface Collection<E> extends Iterable<E> { ... default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } } ✔ Multiple inheritance of type since Java 1.0 ✔ Java 8 introduces multiple inheritance of behavior ✔ No multiple inheritance of state (like in Scala's traits)
  20. 20. Default methods resolution rules 1.Classes always win: a method declaration in the class or a superclass takes priority over any default method declaration. 2.Then sub-interfaces win: the method with the same signature in the most specific default-providing interface is selected. 3.If the choice is still ambiguous, the class inheriting from multiple interfaces has to explicitly select which default method implementation to use by overriding it and calling the desired method explicitly. public interface A { default void hello() { System.out.println("Hello from A"); } } public interface B { default void hello() { System.out.println("Hello from B"); } } public class C implements B, A { void hello() { B.super.hello(); } }
  21. 21. Example 3 Stream laziness
  22. 22. Intermediate & Terminal Operations Return the final result of the operation pipeline Intermediate distinct map, flatMap limit, skip peek sorted Terminal collect count forEach min, max reduce toArray findAny, findFirst allMatch, anyMatch, noneMatch Return another Stream and then can be chained to form a pipeline of operations
  23. 23. map : an intermediate operation map( -> ) Stream
  24. 24. reduce : a terminal operation Stream Integer reduce(0, (a, b) -> a + b)
  25. 25. Putting them all together List<String> strings = asList("Lambda", "expressions", "are", "easy", "and", "useful"); int totalLength = strings.stream() .map(String::length) .reduce(0, (a, b) -> a + b);
  26. 26. Stream Operations Pipelining Intermediate Operations are lazy and return another Stream allowing to fluently pipeline other operations Terminal Operations are eager and computes the result of the whole pipeline
  27. 27. Example 4 Prime numbers
  28. 28. Separation of Concerns List<String> errors = new ArrayList<>(); int errorCount = 0; File file = new File(fileName); String line = file.readLine(); while (errorCount < 40 && line != null) { if (line.startsWith("ERROR")) { errors.add(line); errorCount++; } line = file.readLine(); List<String> errors = Files.lines(Paths.get(fileName)) .filter(l -> l.startsWith("ERROR") .limit(40) .collect(toList()); }
  29. 29. Example 5 Stream from a File
  30. 30. Grouping Map<Dish.Type, List<Dish>> dishesByType = new HashMap<>(); for (Dish dish : menu) { Dish.Type type = dish.getType(); List<Dish> dishes = dishesByType.get(type); if (dishes == null) { dishes = new ArrayList<>(); dishesByType.put(type, dishes); } dishes.add(dish); }
  31. 31. Grouping with Collectors apply key classify item into list next item grouping Map fish meat other salmon pizza rice french fries pork beef chicken prawns Classification Function fish Stream Map<Dish.Type, List<Dish>> dishesByType = menu.stream() .collect(groupingBy(Dish::getType));
  32. 32. Example 6 Grouping
  33. 33. groupingBy Stream Dish::getType classification function Complex Grouping subStream subStream subStream collectingAndThen reducing result Optional[pork] Optional::get result grouping Map collectingAndThen fish meat other salmon pork pizza reducing collectingAndThen reducing result result The original Stream is divided in subStreams according to the classification function Each subStreams is independently processed by the second Collector transformation function The reducing Collector returns the most caloric Dish wrapped in an Optional The collectingAndThen Collector returns the value extracted from the former Optional The results of the 2nd level Collectors become the values of the grouping map
  34. 34. Streams – Parallelism for free parallelStream() employees.stream() .filter(e -> e.getRole() == Role.MANAGER) .map(Employee::getIncome) .reduce(0, (a, b) -> a + b);
  35. 35. A parallel reduction
  36. 36. Example 7 Parallel Streams
  37. 37. Is there such thing as a free lunch?
  38. 38. Probably yes … … but we need functional forks and knives to eat it
  39. 39. Concurrency & Parallelism Parallel programming Running multiple tasks at the same time Concurrent programming Managing concurrent requests Both are hard!
  40. 40. The cause of the problem … Mutable state + Parallel processing = Non-determinism Functional Programming
  41. 41. Race conditions Deadlocks Starvation Livelocks … and its effects Too hard to think about them!
  42. 42. The native Java concurrency model Based on: Locks Synchronization They are sometimes plain evil … Threads Semaphores … and sometimes a necessary pain … … but always the wrong default
  43. 43. Different concurrency models Isolated mutable state (actors) Purely immutable (pure functions) Shared mutable state (threads + locks)
  44. 44. Summing attendants ages (Threads) class Blackboard { int sum = 0; int read() { return sum; } void write(int value) { sum = value; } } class Attendant implements Runnable { int age; Blackboard blackboard; public void run() { synchronized(blackboard) { int oldSum = blackboard.read(); int newSum = oldSum + age; blackboard.write(newSum); } } }
  45. 45. Summing attendants ages (Actors) class Blackboard extends UntypedActors { int sum = 0; public void onReceive(Object message) { if (message instanceof Integer) { sum += (Integer)message; } } } class Attendant { int age; Blackboard blackboard; public void sendAge() { blackboard.sendOneWay(age); } }
  46. 46. Summing attendants ages (Functional) class Blackboard { final int sum; Blackboard(int sum) { this.sum = sum; } } class Attendant { int age; Attendant next; public Blackboard addMyAge(Blackboard blackboard) { final Blackboard b = new Blackboard(blackboard.sum + age); return next == null ? b : next.myAge(b); } }
  47. 47. The state quadrants Mutable Immutable Shared Unshared Actors Threads Functional Programming Determinism Non-determinism
  48. 48. What is a functional program? A program created using only pure functions No side effects allowed like: } }avoidable Reassigning a variable Modifying a data structure in place Setting a field on an object Throwing an exception or halting with an error Printing to the console Reading user input Reading from or writing to a file Drawing on the screen deferrable Functional programming is a restriction on how we write programs, but not on what they can do
  49. 49. Example 8 Modularity to confine side-effects
  50. 50. A pure functional core functional core a thin external layer to handle side-effects Any function with side-effects can be split into a pure function at the core and a pair of functions with side-effect. This transformation can be repeated to push side-effects to the outer layers of the program.
  51. 51. What is a (pure) function? A function with input type A and output type B is a computation which relates every value a of type A to exactly one value b of type B such that b is determined solely by the value of a But, if it really is a function, it will do nothing else
  52. 52. Referential transparency An expression e is referentially transparent if for all programs p, all occurrences of e in p can be replaced by the result of evaluating e, without affecting the observable behavior of p A function f is pure if the expression f(x) is referentially transparent for all referentially transparent x
  53. 53. RT String x = "purple"; String r1 = x.replace('p', 't'); String r2 = x.replace('p', 't'); String r1 = "purple".replace('p', 't'); r1: "turtle" String r2 = "purple".replace('p', 't'); r2: "turtle" Non-RT StringBuilder x = new StringBuilder("Hi"); StringBuilder y = x.append(", mom"); String r1 = y.toString(); String r2 = y.toString(); String r1 = x.append(", mom").toString(); r1: "Hi, mom" String r2 = x.append(", mom").toString(); r1: "Hi, mom, mom" vs.
  54. 54. RT wins Under a developer point of view: Easier to reason about since effects of evaluation are purely local Use of the substitution model: it's possible to replace a term with an equivalent one Under a performance point of view: The JVM is free to optimize the code by safely reordering the instructions No need to synchronize access to shared data Possible to cache the result of time consuming functions (memoization), e.g. with Map.computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
  55. 55. Mutability Parameter binding is about assigning names to things Mutating variables is about assigning things to names Does that second one sound weird? … well it's because it IS weird
  56. 56. Immutability Immutable objects can be shared among many threads exactly because none of them can modify it In the same way immutable (persistent) data structures can be shared without any need to synchronize the different threads accessing them
  57. 57. Old Root New Root 5 8 7 9 3 4 E E E E E E E 5 3 2 E E Persistent Collections Shared data
  58. 58. Example 9 Avoiding mutability
  59. 59. NullPointerException Raise your hand if you've ever seen this
  60. 60. Null references? No, Thanks ✗ Errors source → NPE is by far the most common exception in Java ✗ Bloatware source → Worsen readability by making necessary to fill our code with null checks ✗ Meaningless → Don't have any semantic meaning and in particular are the wrong way to model the absence of a value in a statically typed language ✗ Breaks Java philosophy → Java always hides pointers to developers, except in one case: the null pointer ✗ A hole in the type system → Null has the bottom type, meaning that it can be assigned to any reference type: this is a problem because, when propagated to another part of the system, you have no idea what that null was initially supposed to be Tony Hoare, who invented the null reference in 1965 while working on an object oriented language called ALGOL W, called its invention his “billion dollar mistake”
  61. 61. Replacing nulls with Optionals If nulls are so problematic why don't we just avoid them? Optional value value Optional EMPTY null Optional is a type that models a possibly missing value
  62. 62. public class Person { private Car car; public Car getCar() { return car; } } public class Car { private Insurance insurance; public Insurance getInsurance() { return insurance; } } public class Insurance { private String name; public String getName() { return name; } } Finding Car's Insurance Name
  63. 63. String getCarInsuranceName(Person person) { if (person != null) { Car car = person.getCar(); if (car != null) { Insurance insurance = car.getInsurance(); if (insurance != null) { return insurance.getName() } } } return "Unknown"; } Attempt 1: deep doubts
  64. 64. Attempt 2: too many choices String getCarInsuranceName(Person person) { if (person == null) { return "Unknown"; } Car car = person.getCar(); if (car == null) { return "Unknown"; } Insurance insurance = car.getInsurance(); if (insurance == null) { return "Unknown"; } return insurance.getName() }
  65. 65. Optional to the rescue public class Optional<T> { private static final Optional<?> EMPTY = new Optional<>(null); private final T value; private Optional(T value) { this.value = value; } public<U> Optional<U> map(Function<? super T, ? extends U> f) { return value == null ? EMPTY : new Optional(f.apply(value)); } public<U> Optional<U> flatMap(Function<? super T, Optional<U>> f) { return value == null ? EMPTY : f.apply(value); } }
  66. 66. public class Person { private Optional<Car> car; public Optional<Car> getCar() { return car; } } public class Car { private Optional<Insurance> insurance; public Optional<Insurance> getInsurance() { return insurance; } } public class Insurance { private String name; public String getName() { return name; } } Rethinking our model Using the type system to model nullable value
  67. 67. String getCarInsuranceName(Optional<Person> person) { return person.flatMap(person -> person.getCar()) .flatMap(car -> car.getInsurance()) .map(insurance -> insurance.getName()) .orElse("Unknown"); } Restoring the sanity
  68. 68. String getCarInsuranceName(Optional<Person> person) { return person.flatMap(person -> person.getCar()) .flatMap(car -> car.getInsurance()) .map(insurance -> insurance.getName()) .orElse("Unknown"); } Restoring the sanity Optional Person
  69. 69. String getCarInsuranceName(Optional<Person> person) { return person.flatMap(person -> person.getCar()) .flatMap(car -> car.getInsurance()) .map(insurance -> insurance.getName()) .orElse("Unknown"); } Restoring the sanity Optional Person flatMap(person -> person.getCar())
  70. 70. String getCarInsuranceName(Optional<Person> person) { return person.flatMap(person -> person.getCar()) .flatMap(car -> car.getInsurance()) .map(insurance -> insurance.getName()) .orElse("Unknown"); } Restoring the sanity Optional flatMap(person -> person.getCar()) Optional Car
  71. 71. String getCarInsuranceName(Optional<Person> person) { return person.flatMap(person -> person.getCar()) .flatMap(car -> car.getInsurance()) .map(insurance -> insurance.getName()) .orElse("Unknown"); } Restoring the sanity Optional flatMap(car -> car.getInsurance()) Car
  72. 72. String getCarInsuranceName(Optional<Person> person) { return person.flatMap(person -> person.getCar()) .flatMap(car -> car.getInsurance()) .map(insurance -> insurance.getName()) .orElse("Unknown"); } Restoring the sanity Optional flatMap(car -> car.getInsurance()) Optional Insurance
  73. 73. String getCarInsuranceName(Optional<Person> person) { return person.flatMap(person -> person.getCar()) .flatMap(car -> car.getInsurance()) .map(insurance -> insurance.getName()) .orElse("Unknown"); } Restoring the sanity Optional map(insurance -> insurance.getName()) Insurance
  74. 74. String getCarInsuranceName(Optional<Person> person) { return person.flatMap(person -> person.getCar()) .flatMap(car -> car.getInsurance()) .map(insurance -> insurance.getName()) .orElse("Unknown"); } Restoring the sanity Optional orElse("Unknown") String
  75. 75. Example 10 Using Optional
  76. 76. Thinking in Functions Learning a new language is relatively easy compared with learning a new paradigm. Functional Programming is more a new way of thinking than a new tool set
  77. 77. Example 11 The Loan Pattern
  78. 78. Example 12 Conditional deferred execution
  79. 79. Futures, weren't they enough? The Future interface was introduced in Java 5 to model an asynchronous computation and then provide an handle to a result that will be made available at some point in the future. But it doesn't allow to: ✗ Combining two asynchronous computations in one ✗ Waiting for the completion of all tasks performed by a set of Futures ✗ Waiting for the completion of only the quickest task in a set of Futures (possibly because they’re trying to calculate the same value in different ways) and retrieving its result ✗ Programmatically completing a Future ✗ Reacting to a Future completion
  80. 80. CompletableFutures to the rescue ✔ Programmatically completing a Future with a result … boolean complete(T value) … or an error boolean completeExceptionally(Throwable ex) ✔ Reacting to Future completion CompletableFuture<Void> thenAccept(Consumer<? super T> action) ✔ Combining 2 async operation in sequence ... static CompletableFuture<U> thenCompose( Function<? super T,? extends CompletionStage<U>> fn) … or in parallel static CompletableFuture<V> thenCombine( CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) ✔ Waiting for the completion of all Futures ... static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) … or only the fastest one static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
  81. 81. Example 13 Putting CompletableFutures at work
  82. 82. Key Takeaways Think in functions Strive for immutability Confine side-effects Avoid blocking code Cede control with higher-order functions Leverage referential transparency Use FP to design more composable and reusable API Model potentially missing values with Optionals … but there are no dogmas Be pragmatic and use the right tool for the job at hand Poly-paradigm programming is more powerful and effective than polyglot programming
  83. 83. The bottom line Java is getting functional EMBRACE IT!
  84. 84. Thanks … Questions? Q A Mario Fusco Red Hat – Senior Software Engineer mario.fusco@gmail.com twitter: @mariofusco code examples @ https://github.com/mariofusco/Java8WS

×