Se ha denunciado esta presentación.
Se está descargando tu SlideShare. ×

Java 8 by example!

Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Cargando en…3
×

Eche un vistazo a continuación

1 de 53 Anuncio
Anuncio

Más Contenido Relacionado

Presentaciones para usted (20)

Similares a Java 8 by example! (20)

Anuncio

Más reciente (20)

Java 8 by example!

  1. 1. JAVA 8 - BY EXAMPLE! PATTERNS AND PRACTICES FOR LAMBDAS, STREAMS, OPTIONAL... Createdby /MarkHarrison @markglh
  2. 2. INTERFACE DEFAULT METHODS Interfaces can now have default & static methods! Allows new interface methods to be added without breaking existing implementations Multiple inheritance! public interface Comparator<T> { default Comparator<T> reversed() { // Implementation here } public static Comparator<T> naturalOrder() { // Implementation here } }
  3. 3. MULTIPLE INHERITANCE... What if a class inherits the same default method from two interfaces??? 1.Class methods always win. Whether it’s this class or the superclass, it will take priority 2.If multiple interfaces de ne the same method with a default implementation, then the most speci c is selected (the child in inheritance terms) 3.If it’s not obvious then the compiler will cry, we need to explicitly choose: INTERFACE.super.METHODNAME
  4. 4. LAMBDAS YOU SAY? Basically just a shorthand method implementation Concise and much improved replacement for Anonymous inner classes Makes it easy to pass behaviour around Pass the behaviour into the method, ips the design on its head Lexically scoped (this is effectively shared with the surrounding method)
  5. 5. SYNTAX PLEASE! (int x, int y) -> x + y Argument types can be inferred by the compiler: (x, y) -> x + y Zero-arg Lambdas are also possible: () -> "Java 8!" But how can we use them? Consumer<String> inferredConsumer = x -> System.out.println(x);
  6. 6. WAT?!?
  7. 7. FUNCTIONAL INTERFACES A Functional Interface is any interface with one Single Abstract Method The parameters that the Lambda accepts and returns must match the interface (including exceptions) @FunctionalInterface annotation is provided to highlight these interfaces, it is just a marker though, any SAM interface will work - the annotation simply enforces this at compile time So... When we pass or assign a Lambda: 1. First the Lambda is converted into a Functional Interface 2. Secondly it is invoked via this generated implementation
  8. 8. LAMBDA EXAMPLE 1 - PREDICATES Have you ever written code like this? @FunctionalInterface //Added in Java8 public interface Predicate<T> { boolean test(T t); } private void printMatchingPeople( List<Person> people, Predicate<Person> predicate) { for (Person person : people) { if (predicate.test(person)) { System.out.println(person); } } }
  9. 9. LAMBDA EXAMPLE 1 - PREDICATES Pre Java 8: public class PersonOver50Predicate implements Predicate<Person> { public boolean test(Person person) { return person.getAge() > 50; } } printMatchingPeople(loadsOfPeople, new PersonOver50Predicate());
  10. 10. LAMBDA EXAMPLE 1 - PREDICATES Java 8: printMatchingPeople(loadsOfPeople, x -> x.getAge() > 50); Notice the signature matches that of the Predicate, the compiler automatically gures out the rest What if we had an existing Predicate we wanted to enhance? Composite Predicates FTW... :-) Predicate<Person> ageCheck = x -> x.getAge() > 50; printMatchingPeople(loadsOfPeople, ageCheck.and(x -> x.getIq() > 100));
  11. 11. LAMBDA EXAMPLE 2 - RUNNABLE Pre Java 8 mess: Runnable r1 = new Runnable() { @Override public void run() { System.out.println("Meh!"); } }; r1.run(); Java 8: Runnable r = () -> System.out.println("Woop!"); r.run();
  12. 12. LAMBDA EXAMPLE 3 - COLLECTIONS Lambdas make lots of new Collection methods possible... List<String> strings = new ArrayList<>(); Collections.addAll(strings, "Java", "7", "FTW"); Do something on every element in the List strings.forEach(x -> System.out.print(x + " ")); //Prints: "Java 7 FTW"
  13. 13. LAMBDA EXAMPLE 4 - MORE COLLECTIONS Replace every matching element in the List strings.replaceAll(x -> x == "7" ? "8" : x); strings.forEach(x -> System.out.print(x + " ")); //Prints: "Java 8 FTW" Remove matching elements from the List strings.removeIf(x -> x == "8"); strings.forEach(x -> System.out.print(x + " ")); //Prints: Java FTW
  14. 14. LAMBDA EXAMPLE 5 - COMPARATORS @FunctionalInterface //Added in Java8 version public interface Comparator<T> { int compare(T o1, T o2); //Java 8 adds loads of default methods }
  15. 15. LAMBDA EXAMPLE 6 - COMPARATORS 2 List<Person> loadsOfPeople = ...; Pre Java 8: public class SortByPersonAge implements Comparator<Person> { public int compare(Person p1, Person p2) { return p1.getAge() - p2.getAge(); } } Collections.sort(loadsOfPeople, new SortByPersonAge()); Java 8: Collections.sort(loadsOfPeople, (p1, p2) -> p1.getAge() - p2.getAge());
  16. 16. LAMBDA EXAMPLE 7 - COMPARATORS 3 As usual in Java 8... the Comparator interface provides plenty of useful default & static methods... //"comparing" static method simplifies creation Comparator<Person> newComparator = Comparator.comparing(e -> e.getIq()); //"thenComparing" combines comparators Collections.sort(loadsOfPeople, newComparator.thenComparing( Comparator.comparing(e -> e.getAge())));
  17. 17. LAMBDA EXAMPLE 8 - COMPARATORS 4 and more... //More useful Collection methods... loadsOfPeople4.sort( Comparator.comparing(e -> e.getIq())); //And finally... Method references loadsOfPeople.sort( Comparator.comparing(Person::getAge));
  18. 18. INTRODUCING METHOD REFERENCES Any method can be automatically “lifted” into a function. It must simply meet contract of the FunctionalInterface Can be easier to debug & test than Lambdas, more descriptive stack traces Promotes re-use, keeping code DRY Uses the "::" syntax
  19. 19. METHOD REFERENCES TYPES Reference to... Example a static method Class::staticMethodName an instance method of a speci c object object::instanceMethodName an instance method of an arbitrary object Class::methodName a constructor ClassName::new
  20. 20. REFERENCE TO A STATIC METHOD A simple reference to a static method //isPersonOver50 is a static method printMatchingPeople(loadsOfPeople, PersonPredicates::isPersonOver50); This is equivalent to printMatchingPeople(loadsOfPeople, x -> x.getAge() > 50);
  21. 21. REFERENCE TO AN INSTANCE METHOD OF A SPECIFIC OBJECT A reference to a method on an object instance List<String> strings = ... //print is a method on the "out" PrintStream object strings.forEach(System.out::print); This is equivalent to strings.forEach(x -> System.out.print(x));
  22. 22. REFERENCE TO AN INSTANCE METHOD OF AN ARBITRARY OBJECT Examine this simpli ed de nition of a map function public interface Function<T,R> { public R apply(T t); } public <T, R> List<R> map(Function<T, R> function, List<T> source) { /* applies the function to each element, converting it from T to R */ }
  23. 23. REFERENCE TO AN INSTANCE METHOD OF AN ARBITRARY OBJECT CONT... Although it looks like we're referencing a Class method, we're invoking an instance method on the object(s) passed in the call List<Person> loadsOfPeople = ... List<Integer> namesOfPeople = map(Person::getAge, loadsOfPeople); This is equivalent to map(person -> person.getAge(), loadsOfPeople);
  24. 24. REFERENCE TO A CONSTRUCTOR Uses the constructor to create new objects, the constructor signature must match that of the @FunctionalInterface List<String> digits = Arrays.asList("1", "2", "3"); //Transforms a String into a new Integer List<Integer> numbers = map(Integer::new, digits); This is equivalent to map(s -> new Integer(s), digits);
  25. 25. WHAT'S WRONG WITH COLLECTIONS Every application uses Collections, however Collections are dif cult to query and aggregate, requiring several levels of iteration and conditionals, basically it’s messy and painful Writing multi-threaded code to do this manually is dif cult to write and maintain Imagine writing this manually Stream<String> words=Stream.of("Java", "8", "FTW"); Map<String, Long> letterToNumberOfOccurrences = words.map(w -> w.split("")) .flatMap(Arrays::stream) .collect(Collectors.groupingBy( Function.identity(), Collectors.counting())); //Prints: //{a=2, T=1, F=1, v=1, W=1, 8=1, J=1}
  26. 26. INTRODUCING STREAMS! A Stream is a conceptually xed data structure in which elements are computed on demand Streams iterate internally, you don’t have to worry about handling the iteration Pipelining: Akin to the “pipe” command in unix, allowing aggregations to be chained together Automatic optimisation: short-circuiting and lazy processing Can be in nite Can be executed in parallel automatically using parallelStream or parallel()
  27. 27. MORE STREAMS... Can be created from multiple sources: Arrays.stream(...), Stream.of(1, 2, 3, 4), Stream.iterate(...), Stream.range(...), Random.ints(), Files.lines(...)... Two types of operations: Intermediate (aggregation): lter, map, atMap, sorted ... Terminal: collect, reduce, forEach, ndAny ... Specialised Streams: IntStream, LongStream and DoubleStream: better performance for those unboxed types
  28. 28. MAP public <R> Stream<R> map(Function<T, R> mapper); The mapper Function converts each element from T to R. The result is then added, as is, to the Stream
  29. 29. FLATMAP public <R> Stream<R> flatMap(Function<T, Stream<R>> mapper); The mapper Function converts each element from T to a Stream of R This is the key difference to map, the function itself returns a Stream rather than one element This Stream is then attened (merged) into the main Stream To put it another way: atMap lets you replace each value of a Stream with another Stream, and then it concatenates all the generated streams into one single stream
  30. 30. REDUCE public T reduce(T identity, BinaryOperator<T> accumulator); public Optional<T> reduce( BinaryOperator<T> accumulator); //This is the function contained in BinaryOperator R apply(T t, U u); Terminal Operation Takes a Stream of values and repeatedly applies the accumulator to reduce them into a single value The accumulator is passed the total so far (T) and the current element (U) If passed, identity provides the initial value, rather than the rst element
  31. 31. REDUCE CONTINUED... int totalAgeUsingReduce = loadsOfPeople.stream() .map(Person::getAge) //contains 5, 10, 15 .reduce(0, (total, current) -> total + current); 1.First we map the Person to the age int 2.reduce then starts at 0, and adds the current element, 5 3.reduce continues by adding the current total 5, to the next element, 10 4. nally reduce adds the current total 15, to the next element 15 5.Tada, we've added up all the ages: 30!
  32. 32. COLLECTORS The collect method is a terminal operation which takes various "recipes", called Collectors for accumulating the elements of a stream into a summary result, or converting to a speci c type (such as a List) List<String> listOfStrings = loadsOfPeople.stream() .map(x -> x.getName()) .collect(Collectors.toList()); The argument passed to collect is an object of type java .util.stream.Collector It describes how to accumulate the elements of a stream into a nal result Can be used for Grouping, Partitioning, Averaging, ...
  33. 33. STREAMS EXAMPLE 1 List<Integer> numbers = Arrays.asList(1, 2 ... 8); List<Integer> twoEvenSquares = numbers.stream() .filter(n -> n % 2 == 0) //Filter odd numbers .map(n -> n * n) ////Multiply by itself .limit(2)//Limit to two results .collect(Collectors.toList()); //Finish! Imagine a println in each step... filtering 1 filtering 2 mapping 2 filtering 3 filtering 4 mapping 4
  34. 34. twoEvenSquares = List[4, 16]
  35. 35. STREAMS EXAMPLE 2 List<String> youngerPeopleSortedByIq = loadsOfPeople.stream() .filter(x -> x.getAge() < 50) .sorted(Comparator .comparing(Person::getIq).reversed()) .map(Person::getName) .collect(Collectors.toList()); 1. Filter out all people older than 50 2. Inverse sort the remaining people by IQ 3. map each person to their name (convert to a Stream<String>) 4. Convert the result to a List
  36. 36. STREAMS EXAMPLE 3 - SUM int combinedAge = loadsOfPeople.stream() .mapToInt(Person::getAge) //returns IntStream .sum(); //this HAS to be a specialised Stream 1. map each person to their age, producing an IntStream 2. sum the results, also supports average
  37. 37. STREAMS EXAMPLE 4 - MAP, REDUCE String xml = "<people>" + loadsOfPeople.stream() .map(x -> "<person>"+ x.getName() +"</person>") .reduce("", String::concat) //start with "" + "</people>"; map each Person to an XML element (<person>Steve</person>), then use String.concat to reduce the Stream into one XML String <people> <person>Dave</person> <person>Helen</person> <person>Laura</person> <person>Ben</person> </people>
  38. 38. STREAMS EXAMPLE 5 - MAP List<Stream<Person>> clonedPeople = loadsOfPeople.stream() .map(person -> Stream.of(person, person.dolly())) .collect(Collectors.toList()); 1.map creates a new Stream containing two people 2.This Stream is then added to the main Stream as-is, leaving us with a pretty useless: List<Stream<Person>>
  39. 39. STREAMS EXAMPLE 6 - FLATMAP List<Person> clonedPeople2 = loadsOfPeople.stream() .flatMap(person -> Stream.of(person, person.dolly())) .collect(Collectors.toList()); 1. atMap combines each element from the new Streams into one Stream<Person> 2.So now we've got what we wanted in the rst place, a List<Person> Sweeet!
  40. 40. STREAMS EXAMPLE 7 - REDUCE int totalAgeUsingReduce = loadsOfPeople.stream() .map(Person::getAge) .reduce((total, current) -> total + current) .get(); //get the result from the Optional This is the same as the previous example, the difference being we don't specify a default value for reduce Not specifying a default value means the result is Optional... if the Stream is empty then so is the result!
  41. 41. STREAMS EXAMPLE 8 - GROUPING Map<Integer, List<Person>> peopleGroupedByAge = loadsOfPeople.stream() .filter(x -> x.getIq() > 110) .collect(Collectors.groupingBy(Person::getAge)); The collect method groups the ltered results by age, producing a Map<age, <Person>> {52=[Person{... age=52, iq=113, gender=MALE}], 60=[Person{... age=60, iq=120, gender=FEMALE}], 28=[Person{... age=28, iq=190, gender=MALE}]}
  42. 42. STREAMS EXAMPLE 9 - PARTITIONING Map<Boolean, List<Person>> peoplePartitionedByAge = loadsOfPeople.stream().filter(x -> x.getIq() > 110) .collect(Collectors .partitioningBy(x -> x.getAge() > 55)); The collect method partitions the ltered results by age The Map will have two entries, true and false, according to the Predicate {false=[Person{... age=28, iq=190, gender=MALE}], true=[Person{... age=60, iq=120, gender=FEMALE}]}
  43. 43. STREAMS EXAMPLE 10 - MULTIPLE GROUPS Map<Integer, Double> peopleGroupedBySexAndAvgAge = loadsOfPeople.stream() .filter(x -> x.getIq() > 110) .collect( Collectors.groupingBy(Person::getAge, Collectors.averagingInt(Person::getIq))); We can group by multiple Collectors Here we group by age and the average IQ of that group {52=113.0, 60=117.5, 28=190.0}
  44. 44. STREAMS EXAMPLE 11 - FINDANY loadsOfPeople.stream() .filter(t -> t.getGender() == Person.Sex.FEMALE) .findAny() .ifPresent(System.out::println); findAny either returns an element or nothing, hence we get an Optional ifPresent executes the Lambda if we get a result
  45. 45. STREAMS EXAMPLE 12 - PARALLEL Lets iterate over 10,000,000 elements! x -> Stream.iterate(1L, i -> i + 1) .limit(x) .reduce(Long::sum).get(); Executes in 80ms - we incur a penalty here because the long is repeatedly boxed and unboxed Executes in 211ms?! It turns out parallel isn't always a free win! x -> Stream.iterate(1L, i -> i + 1) .parallel().limit(x) .reduce(Long::sum).get();
  46. 46. STREAMS EXAMPLE 13 - PARALLEL WIN! x -> LongStream.rangeClosed(1L, x) .reduce(Long::sum).getAsLong(); Executes in 24ms - much better using an unboxed Stream Executes in 7ms - now that the Stream isn't dynamic, parallel works much better! x -> LongStream.rangeClosed(1L, x) .parallel() .reduce(Long::sum).getAsLong();
  47. 47. OPTIONAL Use Optional instead of passing null around, helps prevent NullPointerExceptions Optional is a container that’s either empty or present, in which case it contains a value So anytime that you’re thinking of return or accepting a null value in a method, use Optional instead! public class Computer { private Optional<Mainboard> mainboard; } public class Mainboard { private Optional<Cpu> cpu; } public class Cpu { private String type; }
  48. 48. USING OPTIONAL Several ways to create an Optional: Optional.of(cpu); //Throws an NPE if cpu is null Optional.ofNullable(cpu); //empty if cpu is null Getting the contents from the Optional: cpu.get(); //get CPU or throw NoSuchElementException cpu.orElse(new Cpu()); //safe get, provides default And more... cpu.isPresent(); //true if present, false if empty cpu.ifPresent(x -> System.out.println(x.getType())); Also supports map, atMap and lter!
  49. 49. OPTIONAL EXAMPLE 1 - BASICS if (mainboard.isPresent() && mainboard.get().getCpu().isPresent()) { mainboard.get().getCpu().get().getType(); } Eww! Lets try something else! ***Fails to compile, calling getType on an Optional... if only we could atten it??? Optional<String> cpuType = mainboard.map(Mainboard::getCpu) .map(Cpu::getType); //Optional<Optional<Cpu>>
  50. 50. OPTIONAL EXAMPLE 2 - FLATMAP Optional<String> stringOptional = mainboard .flatMap(Mainboard::getCpu) .map(Cpu::getType); Lets take this further and safely get cpu type of a Computer! Computer computer = new Computer(mainboard); String cpu = computer.getMainboard() .flatMap(Mainboard::getCpu) .map(Cpu::getType) .filter(x -> "Intel".equals(x)) .orElse("Nothing we're interested in!");
  51. 51. IT'S BEEN EMOTIONAL... Slides at Source at Follow me markglh.github.io/Java8Madlab-Slides github.com/markglh/Java8Madlab-Examples @markglh
  52. 52. RECOMMENDED READING Lambdas Part 1 (Ted Neward) Lambdas Part 2 (Ted Neward) Lambda Expressions vs Method References (Edwin Dalorzo) Youtube: Java 8 - more readable and exible code (Raoul- Gabriel Urma) Youtube: Lambda Expressions & Streams (Adib Saikali) Java 8 Streams Part 1 (Raoul-Gabriel Urma) Java 8 Streams Part 2 (Raoul-Gabriel Urma) Optional - No more NPE's (Raoul-Gabriel Urma)

×