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

Functional Java 8 - Introduction

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

Eche un vistazo a continuación

1 de 157 Anuncio

Functional Java 8 - Introduction

Descargar para leer sin conexión

Introduction to functional idioms in Java 8, language-extending functional libraries and short overview of reasons for adoption of such programming style.

Examples and snippets available here: https://github.com/lbialy/functionaljava8

Introduction to functional idioms in Java 8, language-extending functional libraries and short overview of reasons for adoption of such programming style.

Examples and snippets available here: https://github.com/lbialy/functionaljava8

Anuncio
Anuncio

Más Contenido Relacionado

Presentaciones para usted (20)

A los espectadores también les gustó (16)

Anuncio

Similares a Functional Java 8 - Introduction (20)

Más reciente (20)

Anuncio

Functional Java 8 - Introduction

  1. 1. Łukasz
 Biały Functional
 Java 8
 
 Introduction Scala / Java Developer lbialy@virtuslab.com
  2. 2. What is functional programming?
  3. 3. What is functional programming? • usage of pure functions
  4. 4. What is functional programming? • usage of pure functions • expressions instead of statements
  5. 5. What is functional programming? • usage of pure functions • expressions instead of statements • widespread immutability
  6. 6. What is functional programming? • usage of pure functions • expressions instead of statements • widespread immutability • referential transparency
  7. 7. What is functional programming? • usage of pure functions • expressions instead of statements • widespread immutability • referential transparency • lazy evaluation
  8. 8. What is functional programming? • usage of pure functions • expressions instead of statements • widespread immutability • referential transparency • lazy evaluation ... and functional idioms!
  9. 9. What's the gain?
  10. 10. What's the gain? • More predictable execution (same arguments always yield the same results)
  11. 11. What's the gain? • More predictable execution (same arguments always yield the same results) • Easier to reason about
  12. 12. What's the gain? • More predictable execution (same arguments always yield the same results) • Easier to reason about • Easier to enforce strict type correctness
  13. 13. What's the gain? • More predictable execution (same arguments always yield the same results) • Easier to reason about • Easier to enforce strict type correctness • more precise checks in compile time
  14. 14. What's the gain? • More predictable execution (same arguments always yield the same results) • Easier to reason about • Easier to enforce strict type correctness • more precise checks in compile time • less bugs in runtime
  15. 15. How does this relate to Java at all?
  16. 16. How does this relate to Java at all? java.lang.NullPointerException at org.springsource.loaded.agent.SpringLoadedPreProcessor.tryToEnsureSystemClassesInitialized(SpringLoadedPreProcessor.java:362) at org.springsource.loaded.agent.SpringLoadedPreProcessor.preProcess(SpringLoadedPreProcessor.java:128) at org.springsource.loaded.agent.ClassPreProcessorAgentAdapter.transform(ClassPreProcessorAgentAdapter.java:102) at sun.instrument.TransformerManager.transform(TransformerManager.java:188) at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:424) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:800) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2818) at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1159) at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1647) at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1526) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:800) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2818) at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1159) at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1647) at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1526) at org.springframework.util.ClassUtils.forName(ClassUtils.java:265) at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:419) at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1299) at org.springframework.beans.factory.support.AbstractBeanFactory.access$000(AbstractBeanFactory.java:109) at org.springframework.beans.factory.support.AbstractBeanFactory$4.run(AbstractBeanFactory.java:1265) at org.springframework.beans.factory.support.AbstractBeanFactory$4.run(AbstractBeanFactory.java:1263) at java.security.AccessController.doPrivileged(Native Method) at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1263) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:575) at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1347) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:358) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:327) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:437) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:626) at com.xxx.core.web.WebApplicationContext.invokeBeanFactoryPostProcessors(WebApplicationContext.java) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:461) at com.xxx.core.web.WebApplicationContext.refresh(WebApplicationContext.java) ...
  17. 17. Most common errors in Java
  18. 18. Most common errors in Java • Takipi monitoring service analysed error logs of over 1k enterprise apps
  19. 19. Most common errors in Java • Takipi monitoring service analysed error logs of over 1k enterprise apps • 29,965,285 exceptions over 30 days
  20. 20. Most common errors in Java • Takipi monitoring service analysed error logs of over 1k enterprise apps • 29,965,285 exceptions over 30 days • Just 10 exception types generated 97,3% of all errors
  21. 21. Most common errors in Java • Takipi monitoring service analysed error logs of over 1k enterprise apps • 29,965,285 exceptions over 30 days • Just 10 exception types generated 97,3% of all errors • Two most common: NullPointerException & NumberFormatException
  22. 22. Most common errors in Java • Takipi monitoring service analysed error logs of over 1k enterprise apps • 29,965,285 exceptions over 30 days • Just 10 exception types generated 97,3% of all errors • Two most common: NullPointerException & NumberFormatException http://blog.takipi.com/we-crunched-1-billion-java-logged-errors-heres-what-causes-97-of-them/
  23. 23. http://blog.takipi.com/the-top-10-exceptions-types-in-production-java-applications-based-on-1b-events/
  24. 24. How can FP help?
  25. 25. How can FP help? • Functional style makes implicit rules governing code explicit, often as a part of type signatures
  26. 26. How can FP help? • Functional style makes implicit rules governing code explicit, often as a part of type signatures • Functional style avoids constructs that are not statically analysable like throwing exceptions, therefore allowing compiler to do it's job better
  27. 27. How can FP help? • Functional style makes implicit rules governing code explicit, often as a part of type signatures • Functional style avoids constructs that are not statically analysable like throwing exceptions, therefore allowing compiler to do it's job better • In concurrent and parallel programming lack of mutable state removes whole classes of programming errors for free
  28. 28. How can FP help? • Functional style makes implicit rules governing code explicit, often as a part of type signatures • Functional style avoids constructs that are not statically analysable like throwing exceptions, therefore allowing compiler to do it's job better • In concurrent and parallel programming lack of mutable state removes whole classes of programming errors for free • Yields an elegant and concise code :)
  29. 29. 1 package com.example; 2 3 class CharacterOps { 4 5 static Character firstCharacter(String string) { 6 return (string.length() > 0) ? string.charAt(0) : null; 7 } 8 9 static Integer getAlphabetPosition(Character character) { 10 int code = (int) character; 11 12 if (isLowerCase(code)) 13 return code - 96; 14 else if (isUpperCase(code)) 15 return code - 64; 16 else 17 return null; // or throw exception? 18 } 19 20 private static boolean isLowerCase(int code) { 21 return code >= 97 && code <= 122; 22 } 23 24 private static boolean isUpperCase(int code) { 25 return code >= 65 && code <= 90; 26 } 27 28 } Optionality
 of values
  30. 30. 1 package com.example; 2 3 class CharacterOps { 4 5 static Character firstCharacter(String string) { 6 return (string.length() > 0) ? string.charAt(0) : null; 7 } 8 9 static Integer getAlphabetPosition(Character character) { 10 int code = (int) character; 11 12 if (isLowerCase(code)) 13 return code - 96; 14 else if (isUpperCase(code)) 15 return code - 64; 16 else 17 return null; // or throw exception? 18 } 19 20 private static boolean isLowerCase(int code) { 21 return code >= 97 && code <= 122; 22 } 23 24 private static boolean isUpperCase(int code) { 25 return code >= 65 && code <= 90; 26 } 27 28 } Optionality
 of values • optionality of value is not explicitly known from method signature
  31. 31. 1 package com.example; 2 3 class CharacterOps { 4 5 static Character firstCharacter(String string) { 6 return (string.length() > 0) ? string.charAt(0) : null; 7 } 8 9 static Integer getAlphabetPosition(Character character) { 10 int code = (int) character; 11 12 if (isLowerCase(code)) 13 return code - 96; 14 else if (isUpperCase(code)) 15 return code - 64; 16 else 17 return null; // or throw exception? 18 } 19 20 private static boolean isLowerCase(int code) { 21 return code >= 97 && code <= 122; 22 } 23 24 private static boolean isUpperCase(int code) { 25 return code >= 65 && code <= 90; 26 } 27 28 } Optionality
 of values • optionality of value is not explicitly known from method signature
  32. 32. 1 package com.example; 2 3 class CharacterOps { 4 5 static Character firstCharacter(String string) { 6 return (string.length() > 0) ? string.charAt(0) : null; 7 } 8 9 static Integer getAlphabetPosition(Character character) { 10 int code = (int) character; 11 12 if (isLowerCase(code)) 13 return code - 96; 14 else if (isUpperCase(code)) 15 return code - 64; 16 else 17 return null; // or throw exception? 18 } 19 20 private static boolean isLowerCase(int code) { 21 return code >= 97 && code <= 122; 22 } 23 24 private static boolean isUpperCase(int code) { 25 return code >= 65 && code <= 90; 26 } 27 28 } Optionality
 of values • optionality of value is not explicitly known from method signature
  33. 33. 1 package com.example; 2 3 class CharacterOps { 4 5 static Character firstCharacter(String string) { 6 return (string.length() > 0) ? string.charAt(0) : null; 7 } 8 9 static Integer getAlphabetPosition(Character character) { 10 int code = (int) character; 11 12 if (isLowerCase(code)) 13 return code - 96; 14 else if (isUpperCase(code)) 15 return code - 64; 16 else 17 return null; // or throw exception? 18 } 19 20 private static boolean isLowerCase(int code) { 21 return code >= 97 && code <= 122; 22 } 23 24 private static boolean isUpperCase(int code) { 25 return code >= 65 && code <= 90; 26 } 27 28 } Optionality
 of values • optionality of value is not explicitly known from method signature • not feeling especially bright today? have a NullPointerException in runtime
  34. 34. 1 package com.example; 2 3 class CharacterOps { 4 5 static Character firstCharacter(String string) { 6 return (string.length() > 0) ? string.charAt(0) : null; 7 } 8 9 static Integer getAlphabetPosition(Character character) { 10 int code = (int) character; 11 12 if (isLowerCase(code)) 13 return code - 96; 14 else if (isUpperCase(code)) 15 return code - 64; 16 else 17 return null; // or throw exception? 18 } 19 20 private static boolean isLowerCase(int code) { 21 return code >= 97 && code <= 122; 22 } 23 24 private static boolean isUpperCase(int code) { 25 return code >= 65 && code <= 90; 26 } 27 28 } Optionality
 of values • optionality of value is not explicitly known from method signature • not feeling especially bright today? have a NullPointerException in runtime
  35. 35. 1 package com.example; 2 3 class CharacterOps { 4 5 static Character firstCharacter(String string) { 6 return (string.length() > 0) ? string.charAt(0) : null; 7 } 8 9 static Integer getAlphabetPosition(Character character) { 10 int code = (int) character; 11 12 if (isLowerCase(code)) 13 return code - 96; 14 else if (isUpperCase(code)) 15 return code - 64; 16 else 17 return null; // or throw exception? 18 } 19 20 private static boolean isLowerCase(int code) { 21 return code >= 97 && code <= 122; 22 } 23 24 private static boolean isUpperCase(int code) { 25 return code >= 65 && code <= 90; 26 } 27 28 } Optionality
 of values • optionality of value is not explicitly known from method signature • not feeling especially bright today? have a NullPointerException in runtime
  36. 36. 1 package com.example; 2 3 class CharacterOps { 4 5 static Character firstCharacter(String string) { 6 return (string.length() > 0) ? string.charAt(0) : null; 7 } 8 9 static Integer getAlphabetPosition(Character character) { 10 int code = (int) character; 11 12 if (isLowerCase(code)) 13 return code - 96; 14 else if (isUpperCase(code)) 15 return code - 64; 16 else 17 return null; // or throw exception? 18 } 19 20 private static boolean isLowerCase(int code) { 21 return code >= 97 && code <= 122; 22 } 23 24 private static boolean isUpperCase(int code) { 25 return code >= 65 && code <= 90; 26 } 27 28 } Optionality
 of values • optionality of value is not explicitly known from method signature • not feeling especially bright today? have a NullPointerException in runtime • null is a subtype of every reference type, so it will be propagated as 
 a valid object value
  37. 37. 1 package com.example; 2 3 class CharacterOps { 4 5 static Character firstCharacter(String string) { 6 return (string.length() > 0) ? string.charAt(0) : null; 7 } 8 9 static Integer getAlphabetPosition(Character character) { 10 int code = (int) character; 11 12 if (isLowerCase(code)) 13 return code - 96; 14 else if (isUpperCase(code)) 15 return code - 64; 16 else 17 return null; // or throw exception? 18 } 19 20 private static boolean isLowerCase(int code) { 21 return code >= 97 && code <= 122; 22 } 23 24 private static boolean isUpperCase(int code) { 25 return code >= 65 && code <= 90; 26 } 27 28 } Optionality
 of values • optionality of value is not explicitly known from method signature • not feeling especially bright today? have a NullPointerException in runtime • null is a subtype of every reference type, so it will be propagated as 
 a valid object value • tracking null's origination point is so much fun!
  38. 38. What we want:
 composability 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class CharOpsTest { 8 9 @Test 10 public void testComposability() { 11 12 getAlphabetPosition(firstCharacter("cat")); 13 14 getAlphabetPosition(firstCharacter("")); 15 16 } 17 18 }
  39. 39. What we want:
 composability 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class CharOpsTest { 8 9 @Test 10 public void testComposability() { 11 12 getAlphabetPosition(firstCharacter("cat")); 13 14 getAlphabetPosition(firstCharacter("")); 15 16 } 17 18 } • feeding function a "cat" is fine, we get 3 as result
  40. 40. What we want:
 composability 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class CharOpsTest { 8 9 @Test 10 public void testComposability() { 11 12 getAlphabetPosition(firstCharacter("cat")); 13 14 getAlphabetPosition(firstCharacter("")); 15 16 } 17 18 } • feeding function a "cat" is fine, we get 3 as result java.lang.NullPointerException at com.example.CharacterOps.getAlphabetPosition(CharacterOps.java:10) at com.example.CharOpsTest.testComposability(CharOpsTest.java:14)
  41. 41. What we want:
 composability 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class CharOpsTest { 8 9 @Test 10 public void testComposability() { 11 12 getAlphabetPosition(firstCharacter("cat")); 13 14 getAlphabetPosition(firstCharacter("")); 15 16 } 17 18 } • feeding function a "cat" is fine, we get 3 as result • doesn't work so well for 
 an empty string though java.lang.NullPointerException at com.example.CharacterOps.getAlphabetPosition(CharacterOps.java:10) at com.example.CharOpsTest.testComposability(CharOpsTest.java:14)
  42. 42. 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class ActualCharOpsTest { 8 9 @Test 10 public void testNullChecks() { 11 12 Character firstChar = firstCharacter("cat"); 13 14 if (null == firstChar) { 15 // oops. what now? exception? propagate null? 16 } else { 17 // will throw NPE on digit :( 18 Integer position = getAlphabetPosition(firstChar); 19 // ... 20 } 21 22 } 23 24 } What we get:
 explicit null checks
  43. 43. 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class ActualCharOpsTest { 8 9 @Test 10 public void testNullChecks() { 11 12 Character firstChar = firstCharacter("cat"); 13 14 if (null == firstChar) { 15 // oops. what now? exception? propagate null? 16 } else { 17 // will throw NPE on digit :( 18 Integer position = getAlphabetPosition(firstChar); 19 // ... 20 } 21 22 } 23 24 } • you have to remember to check for null explicitly or face NPE hunting What we get:
 explicit null checks
  44. 44. 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class ActualCharOpsTest { 8 9 @Test 10 public void testNullChecks() { 11 12 Character firstChar = firstCharacter("cat"); 13 14 if (null == firstChar) { 15 // oops. what now? exception? propagate null? 16 } else { 17 // will throw NPE on digit :( 18 Integer position = getAlphabetPosition(firstChar); 19 // ... 20 } 21 22 } 23 24 } • you have to remember to check for null explicitly or face NPE hunting • methods don't compose anymore What we get:
 explicit null checks
  45. 45. 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class ActualCharOpsTest { 8 9 @Test 10 public void testNullChecks() { 11 12 Character firstChar = firstCharacter("cat"); 13 14 if (null == firstChar) { 15 // oops. what now? exception? propagate null? 16 } else { 17 // will throw NPE on digit :( 18 Integer position = getAlphabetPosition(firstChar); 19 // ... 20 } 21 22 } 23 24 } • you have to remember to check for null explicitly or face NPE hunting • methods don't compose anymore • ugly :( What we get:
 explicit null checks
  46. 46. 1 package com.example; 2 3 import java.util.Optional; 4 5 class SafeCharOps { 6 7 static Optional<Character> firstCharacter( 8 String string 9 ) { 10 return (string.length() > 0) ? 11 Optional.of(string.charAt(0)) : 12 Optional.empty(); 13 } 14 15 static Optional<Integer> getAlphabetPosition( 16 Character character 17 ) { 18 int code = (int) character; 19 20 return isLowerCase(code) ? Optional.of(code - 96) : 21 isUpperCase(code) ? Optional.of(code - 64) : 22 Optional.empty(); 23 } 24 25 private static boolean isLowerCase(int code) { 26 return code >= 97 && code <= 122; 27 } 28 29 private static boolean isUpperCase(int code) { 30 return code >= 65 && code <= 90; 31 } 32 33 } Introducing 
 Optional<T>
  47. 47. 1 package com.example; 2 3 import java.util.Optional; 4 5 class SafeCharOps { 6 7 static Optional<Character> firstCharacter( 8 String string 9 ) { 10 return (string.length() > 0) ? 11 Optional.of(string.charAt(0)) : 12 Optional.empty(); 13 } 14 15 static Optional<Integer> getAlphabetPosition( 16 Character character 17 ) { 18 int code = (int) character; 19 20 return isLowerCase(code) ? Optional.of(code - 96) : 21 isUpperCase(code) ? Optional.of(code - 64) : 22 Optional.empty(); 23 } 24 25 private static boolean isLowerCase(int code) { 26 return code >= 97 && code <= 122; 27 } 28 29 private static boolean isUpperCase(int code) { 30 return code >= 65 && code <= 90; 31 } 32 33 } • optionality of return value is explicit in type signature Introducing 
 Optional<T>
  48. 48. 1 package com.example; 2 3 import java.util.Optional; 4 5 class SafeCharOps { 6 7 static Optional<Character> firstCharacter( 8 String string 9 ) { 10 return (string.length() > 0) ? 11 Optional.of(string.charAt(0)) : 12 Optional.empty(); 13 } 14 15 static Optional<Integer> getAlphabetPosition( 16 Character character 17 ) { 18 int code = (int) character; 19 20 return isLowerCase(code) ? Optional.of(code - 96) : 21 isUpperCase(code) ? Optional.of(code - 64) : 22 Optional.empty(); 23 } 24 25 private static boolean isLowerCase(int code) { 26 return code >= 97 && code <= 122; 27 } 28 29 private static boolean isUpperCase(int code) { 30 return code >= 65 && code <= 90; 31 } 32 33 } • optionality of return value is explicit in type signature • expressions used where possible Introducing 
 Optional<T>
  49. 49. Composability revisited 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class CharOpsTest { 8 9 @Test 10 public void testComposability() { 11 12 getAlphabetPosition(firstCharacter("cat")); 13 14 getAlphabetPosition(firstCharacter("")); 15 16 } 17 18 }
  50. 50. Composability revisited 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class CharOpsTest { 8 9 @Test 10 public void testComposability() { 11 12 getAlphabetPosition(firstCharacter("cat")); 13 14 getAlphabetPosition(firstCharacter("")); 15 16 } 17 18 } • doesn't compile :(
  51. 51. Composability revisited 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class CharOpsTest { 8 9 @Test 10 public void testComposability() { 11 12 getAlphabetPosition(firstCharacter("cat")); 13 14 getAlphabetPosition(firstCharacter("")); 15 16 } 17 18 } • doesn't compile :( • getAlphabetPosition accepts Character instances
  52. 52. Composability revisited 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class CharOpsTest { 8 9 @Test 10 public void testComposability() { 11 12 getAlphabetPosition(firstCharacter("cat")); 13 14 getAlphabetPosition(firstCharacter("")); 15 16 } 17 18 } • doesn't compile :( • getAlphabetPosition accepts Character instances • we have Optional<Character> from
 firstCharacter call
  53. 53. Composability revisited 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.CharacterOps.*; 6 7 public class CharOpsTest { 8 9 @Test 10 public void testComposability() { 11 12 getAlphabetPosition(firstCharacter("cat")); 13 14 getAlphabetPosition(firstCharacter("")); 15 16 } 17 18 } • doesn't compile :( • getAlphabetPosition accepts Character instances • we have Optional<Character> from
 firstCharacter call • how to extract value in safe manner?
  54. 54. 1 package com.example; 2 3 import org.junit.Test; 4 5 import java.util.Optional; 6 7 import static com.example.SafeCharOps.*; 8 9 public class OptionalCharOpsTest { 10 11 @Test 12 public void testOptionalMethods() { 13 14 Optional<Character> firstChar = 15 firstCharacter("cat"); 16 17 if (firstChar.isPresent()) { 18 19 Optional<Integer> alphabetPosition = 20 getAlphabetPosition(firstChar.get()); 21 22 if (alphabetPosition.isPresent()) { 23 Integer position = alphabetPosition.get(); 24 25 // ... 26 } 27 28 } 29 30 } 31 32 } Imperative 
 approach
  55. 55. 1 package com.example; 2 3 import org.junit.Test; 4 5 import java.util.Optional; 6 7 import static com.example.SafeCharOps.*; 8 9 public class OptionalCharOpsTest { 10 11 @Test 12 public void testOptionalMethods() { 13 14 Optional<Character> firstChar = 15 firstCharacter("cat"); 16 17 if (firstChar.isPresent()) { 18 19 Optional<Integer> alphabetPosition = 20 getAlphabetPosition(firstChar.get()); 21 22 if (alphabetPosition.isPresent()) { 23 Integer position = alphabetPosition.get(); 24 25 // ... 26 } 27 28 } 29 30 } 31 32 } • we've gained explicit information about optionality Imperative 
 approach
  56. 56. 1 package com.example; 2 3 import org.junit.Test; 4 5 import java.util.Optional; 6 7 import static com.example.SafeCharOps.*; 8 9 public class OptionalCharOpsTest { 10 11 @Test 12 public void testOptionalMethods() { 13 14 Optional<Character> firstChar = 15 firstCharacter("cat"); 16 17 if (firstChar.isPresent()) { 18 19 Optional<Integer> alphabetPosition = 20 getAlphabetPosition(firstChar.get()); 21 22 if (alphabetPosition.isPresent()) { 23 Integer position = alphabetPosition.get(); 24 25 // ... 26 } 27 28 } 29 30 } 31 32 } • we've gained explicit information about optionality • still looks like null checks :( Imperative 
 approach
  57. 57. 1 package com.example; 2 3 import org.junit.Test; 4 5 import java.util.Optional; 6 7 import static com.example.SafeCharOps.*; 8 9 public class OptionalCharOpsTest { 10 11 @Test 12 public void testOptionalMethods() { 13 14 Optional<Character> firstChar = 15 firstCharacter("cat"); 16 17 if (firstChar.isPresent()) { 18 19 Optional<Integer> alphabetPosition = 20 getAlphabetPosition(firstChar.get()); 21 22 if (alphabetPosition.isPresent()) { 23 Integer position = alphabetPosition.get(); 24 25 // ... 26 } 27 28 } 29 30 } 31 32 } • we've gained explicit information about optionality • still looks like null checks :( • still breaks composability :( Imperative 
 approach
  58. 58. Functional 
 approach 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.SafeCharOps.*; 6 7 public class OptionalCharOpsTest { 8 9 @Test 10 public void testOptionalMethods() { 11 12 firstCharacter("cat") 13 .flatMap(SafeCharOps::getAlphabetPosition) 14 .ifPresent(value -> { 15 assert value == 3; 16 }); 17 18 assert !firstCharacter("") 19 .flatMap(SafeCharOps::getAlphabetPosition) 20 .isPresent(); 21 } 22 23 }
  59. 59. • no null checks! Functional 
 approach 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.SafeCharOps.*; 6 7 public class OptionalCharOpsTest { 8 9 @Test 10 public void testOptionalMethods() { 11 12 firstCharacter("cat") 13 .flatMap(SafeCharOps::getAlphabetPosition) 14 .ifPresent(value -> { 15 assert value == 3; 16 }); 17 18 assert !firstCharacter("") 19 .flatMap(SafeCharOps::getAlphabetPosition) 20 .isPresent(); 21 } 22 23 }
  60. 60. • no null checks! • clean functional composition Functional 
 approach 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.SafeCharOps.*; 6 7 public class OptionalCharOpsTest { 8 9 @Test 10 public void testOptionalMethods() { 11 12 firstCharacter("cat") 13 .flatMap(SafeCharOps::getAlphabetPosition) 14 .ifPresent(value -> { 15 assert value == 3; 16 }); 17 18 assert !firstCharacter("") 19 .flatMap(SafeCharOps::getAlphabetPosition) 20 .isPresent(); 21 } 22 23 }
  61. 61. • no null checks! • clean functional composition • type safety, IDE helps at every step as type inference is easy Functional 
 approach 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.SafeCharOps.*; 6 7 public class OptionalCharOpsTest { 8 9 @Test 10 public void testOptionalMethods() { 11 12 firstCharacter("cat") 13 .flatMap(SafeCharOps::getAlphabetPosition) 14 .ifPresent(value -> { 15 assert value == 3; 16 }); 17 18 assert !firstCharacter("") 19 .flatMap(SafeCharOps::getAlphabetPosition) 20 .isPresent(); 21 } 22 23 }
  62. 62. • no null checks! • clean functional composition • type safety, IDE helps at every step as type inference is easy • easier to refactor Functional 
 approach 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.SafeCharOps.*; 6 7 public class OptionalCharOpsTest { 8 9 @Test 10 public void testOptionalMethods() { 11 12 firstCharacter("cat") 13 .flatMap(SafeCharOps::getAlphabetPosition) 14 .ifPresent(value -> { 15 assert value == 3; 16 }); 17 18 assert !firstCharacter("") 19 .flatMap(SafeCharOps::getAlphabetPosition) 20 .isPresent(); 21 } 22 23 }
  63. 63. • no null checks! • clean functional composition • type safety, IDE helps at every step as type inference is easy • easier to refactor • elegant & succinct Functional 
 approach 1 package com.example; 2 3 import org.junit.Test; 4 5 import static com.example.SafeCharOps.*; 6 7 public class OptionalCharOpsTest { 8 9 @Test 10 public void testOptionalMethods() { 11 12 firstCharacter("cat") 13 .flatMap(SafeCharOps::getAlphabetPosition) 14 .ifPresent(value -> { 15 assert value == 3; 16 }); 17 18 assert !firstCharacter("") 19 .flatMap(SafeCharOps::getAlphabetPosition) 20 .isPresent(); 21 } 22 23 }
  64. 64. 1 package com.example; 2 3 import java.util.List; 4 import java.util.stream.Collectors; 5 6 class StreamsAndErrors { 7 8 static List<Integer> toNumbers(List<String> strings) { 9 return strings.stream() 10 .map(Integer::parseInt) 11 .collect(Collectors.toList()); 12 } 13 14 } Streams API
 revisited: errors 1 package com.example; 2 3 import org.junit.Test; 4 5 import java.util.List; 6 7 import static java.lang.System.out; 8 import static java.util.Arrays.asList; 9 10 public class StreamsTest { 11 12 @Test 13 public void testNumberConversion() { 14 List<String> strings = asList("1", "2", "3"); 15 out.println(StreamsAndErrors.toNumbers(strings)); 16 // prints '[1, 2, 3]' 17 18 List<String> itsaTrap = asList("1", "a", "3"); 19 out.println(StreamsAndErrors.toNumbers(itsaTrap)); 20 // NumberFormatException: For input string: "a" 21 } 22 23 }
  65. 65. 1 package com.example; 2 3 import java.util.List; 4 import java.util.stream.Collectors; 5 6 class StreamsAndErrors { 7 8 static List<Integer> toNumbers(List<String> strings) { 9 return strings.stream() 10 .map(Integer::parseInt) 11 .collect(Collectors.toList()); 12 } 13 14 } • generic string to integer conversion on collection level Streams API
 revisited: errors 1 package com.example; 2 3 import org.junit.Test; 4 5 import java.util.List; 6 7 import static java.lang.System.out; 8 import static java.util.Arrays.asList; 9 10 public class StreamsTest { 11 12 @Test 13 public void testNumberConversion() { 14 List<String> strings = asList("1", "2", "3"); 15 out.println(StreamsAndErrors.toNumbers(strings)); 16 // prints '[1, 2, 3]' 17 18 List<String> itsaTrap = asList("1", "a", "3"); 19 out.println(StreamsAndErrors.toNumbers(itsaTrap)); 20 // NumberFormatException: For input string: "a" 21 } 22 23 }
  66. 66. 1 package com.example; 2 3 import java.util.List; 4 import java.util.stream.Collectors; 5 6 class StreamsAndErrors { 7 8 static List<Integer> toNumbers(List<String> strings) { 9 return strings.stream() 10 .map(Integer::parseInt) 11 .collect(Collectors.toList()); 12 } 13 14 } • generic string to integer conversion on collection level • type signature tells nothing about possible of failure Streams API
 revisited: errors 1 package com.example; 2 3 import org.junit.Test; 4 5 import java.util.List; 6 7 import static java.lang.System.out; 8 import static java.util.Arrays.asList; 9 10 public class StreamsTest { 11 12 @Test 13 public void testNumberConversion() { 14 List<String> strings = asList("1", "2", "3"); 15 out.println(StreamsAndErrors.toNumbers(strings)); 16 // prints '[1, 2, 3]' 17 18 List<String> itsaTrap = asList("1", "a", "3"); 19 out.println(StreamsAndErrors.toNumbers(itsaTrap)); 20 // NumberFormatException: For input string: "a" 21 } 22 23 }
  67. 67. 1 package com.example; 2 3 import java.util.List; 4 import java.util.stream.Collectors; 5 6 class StreamsAndErrors { 7 8 static List<Integer> toNumbers(List<String> strings) { 9 return strings.stream() 10 .map(Integer::parseInt) 11 .collect(Collectors.toList()); 12 } 13 14 } • generic string to integer conversion on collection level • type signature tells nothing about possible of failure • programmer knows (let's hope so) about possibility of failure and has to deal with it explicitly Streams API
 revisited: errors 1 package com.example; 2 3 import org.junit.Test; 4 5 import java.util.List; 6 7 import static java.lang.System.out; 8 import static java.util.Arrays.asList; 9 10 public class StreamsTest { 11 12 @Test 13 public void testNumberConversion() { 14 List<String> strings = asList("1", "2", "3"); 15 out.println(StreamsAndErrors.toNumbers(strings)); 16 // prints '[1, 2, 3]' 17 18 List<String> itsaTrap = asList("1", "a", "3"); 19 out.println(StreamsAndErrors.toNumbers(itsaTrap)); 20 // NumberFormatException: For input string: "a" 21 } 22 23 }
  68. 68. 1 package com.example; 2 3 import java.util.List; 4 import java.util.Objects; 5 import java.util.Optional; 6 import java.util.stream.Collectors; 7 8 class StreamsAndErrors { 9 10 static List<Integer> filterOut(List<String> strings) { 11 return strings.stream() 12 .map(s -> { 13 try { 14 return Integer.parseInt(s); 15 } catch (NumberFormatException nfe) { 16 return null; 17 } 18 }) 19 .filter(Objects::nonNull) 20 .collect(Collectors.toList()); 21 } 22 23 static Optional<List<Integer>> failOnFirst( 24 List<String> strings 25 ) { 26 try { 27 return Optional.of( 28 strings.stream() 29 .map(Integer::parseInt) 30 .collect(Collectors.toList()) 31 ); 32 } catch (NumberFormatException nfe) { 33 return Optional.empty(); 34 } 35 } 36 37 } Handling errors 
 with try
  69. 69. 1 package com.example; 2 3 import java.util.List; 4 import java.util.Objects; 5 import java.util.Optional; 6 import java.util.stream.Collectors; 7 8 class StreamsAndErrors { 9 10 static List<Integer> filterOut(List<String> strings) { 11 return strings.stream() 12 .map(s -> { 13 try { 14 return Integer.parseInt(s); 15 } catch (NumberFormatException nfe) { 16 return null; 17 } 18 }) 19 .filter(Objects::nonNull) 20 .collect(Collectors.toList()); 21 } 22 23 static Optional<List<Integer>> failOnFirst( 24 List<String> strings 25 ) { 26 try { 27 return Optional.of( 28 strings.stream() 29 .map(Integer::parseInt) 30 .collect(Collectors.toList()) 31 ); 32 } catch (NumberFormatException nfe) { 33 return Optional.empty(); 34 } 35 } 36 37 } • ughh... null again :( Handling errors 
 with try
  70. 70. 1 package com.example; 2 3 import java.util.List; 4 import java.util.Objects; 5 import java.util.Optional; 6 import java.util.stream.Collectors; 7 8 class StreamsAndErrors { 9 10 static List<Integer> filterOut(List<String> strings) { 11 return strings.stream() 12 .map(s -> { 13 try { 14 return Integer.parseInt(s); 15 } catch (NumberFormatException nfe) { 16 return null; 17 } 18 }) 19 .filter(Objects::nonNull) 20 .collect(Collectors.toList()); 21 } 22 23 static Optional<List<Integer>> failOnFirst( 24 List<String> strings 25 ) { 26 try { 27 return Optional.of( 28 strings.stream() 29 .map(Integer::parseInt) 30 .collect(Collectors.toList()) 31 ); 32 } catch (NumberFormatException nfe) { 33 return Optional.empty(); 34 } 35 } 36 37 } • ughh... null again :( • we have to filter nulls out lest we create a disaster waiting to happen Handling errors 
 with try
  71. 71. 1 package com.example; 2 3 import java.util.List; 4 import java.util.Objects; 5 import java.util.Optional; 6 import java.util.stream.Collectors; 7 8 class StreamsAndErrors { 9 10 static List<Integer> filterOut(List<String> strings) { 11 return strings.stream() 12 .map(s -> { 13 try { 14 return Integer.parseInt(s); 15 } catch (NumberFormatException nfe) { 16 return null; 17 } 18 }) 19 .filter(Objects::nonNull) 20 .collect(Collectors.toList()); 21 } 22 23 static Optional<List<Integer>> failOnFirst( 24 List<String> strings 25 ) { 26 try { 27 return Optional.of( 28 strings.stream() 29 .map(Integer::parseInt) 30 .collect(Collectors.toList()) 31 ); 32 } catch (NumberFormatException nfe) { 33 return Optional.empty(); 34 } 35 } 36 37 } • ughh... null again :( • we have to filter nulls out lest we create a disaster waiting to happen • using Optional tells caller that this method might yield value, but doesn't say anything about errors that may happen inside Handling errors 
 with try
  72. 72. Handling errors 
 with Try 1 package com.example; 2 3 import javaslang.control.Try; 4 5 import java.util.List; 6 import java.util.stream.Collectors; 7 8 class StreamsAndErrors { 9 10 static List<Try<Integer>> withErrs(List<String> strings) { 11 return strings.stream() 12 .map(s -> Try.of(() -> Integer.parseInt(s))) 13 .collect(Collectors.toList()); 14 } 15 16 static Try<List<Integer>> failOnFirst( 17 List<String> strings 18 ) { 19 return Try.of(() -> strings.stream() 20 .map(Integer::parseInt) 21 .collect(Collectors.toList())); 22 } 23 24 }
  73. 73. • type signature explicitly informs us about the possibility of error Handling errors 
 with Try 1 package com.example; 2 3 import javaslang.control.Try; 4 5 import java.util.List; 6 import java.util.stream.Collectors; 7 8 class StreamsAndErrors { 9 10 static List<Try<Integer>> withErrs(List<String> strings) { 11 return strings.stream() 12 .map(s -> Try.of(() -> Integer.parseInt(s))) 13 .collect(Collectors.toList()); 14 } 15 16 static Try<List<Integer>> failOnFirst( 17 List<String> strings 18 ) { 19 return Try.of(() -> strings.stream() 20 .map(Integer::parseInt) 21 .collect(Collectors.toList())); 22 } 23 24 }
  74. 74. • type signature explicitly informs us about the possibility of error • exceptions are caught inside stream or in lambda wrapping whole stream processing Handling errors 
 with Try 1 package com.example; 2 3 import javaslang.control.Try; 4 5 import java.util.List; 6 import java.util.stream.Collectors; 7 8 class StreamsAndErrors { 9 10 static List<Try<Integer>> withErrs(List<String> strings) { 11 return strings.stream() 12 .map(s -> Try.of(() -> Integer.parseInt(s))) 13 .collect(Collectors.toList()); 14 } 15 16 static Try<List<Integer>> failOnFirst( 17 List<String> strings 18 ) { 19 return Try.of(() -> strings.stream() 20 .map(Integer::parseInt) 21 .collect(Collectors.toList())); 22 } 23 24 }
  75. 75. 1 package com.example; 2 3 import java.text.SimpleDateFormat; 4 import java.util.Date; 5 import java.util.List; 6 import java.util.stream.Collectors; 7 8 class StreamsAndCheckedExceptions { 9 10 private static SimpleDateFormat sdf = 11 new SimpleDateFormat("ddMMyyyy"); 12 13 static List<Date> parseAll(List<String> strings) { 14 return strings.stream() 15 .map(sdf::parse) // won't compile! 16 .collect(Collectors.toList()); 17 } 18 19 } Checked exceptions 
 & Java 8 Streams:
 Rough edges
  76. 76. 1 package com.example; 2 3 import java.text.SimpleDateFormat; 4 import java.util.Date; 5 import java.util.List; 6 import java.util.stream.Collectors; 7 8 class StreamsAndCheckedExceptions { 9 10 private static SimpleDateFormat sdf = 11 new SimpleDateFormat("ddMMyyyy"); 12 13 static List<Date> parseAll(List<String> strings) { 14 return strings.stream() 15 .map(sdf::parse) // won't compile! 16 .collect(Collectors.toList()); 17 } 18 19 } • ParseException is checked, which breaks lambda expression Checked exceptions 
 & Java 8 Streams:
 Rough edges
  77. 77. 1 package com.example; 2 3 import java.text.SimpleDateFormat; 4 import java.util.Date; 5 import java.util.List; 6 import java.util.stream.Collectors; 7 8 class StreamsAndCheckedExceptions { 9 10 private static SimpleDateFormat sdf = 11 new SimpleDateFormat("ddMMyyyy"); 12 13 static List<Date> parseAll(List<String> strings) { 14 return strings.stream() 15 .map(sdf::parse) // won't compile! 16 .collect(Collectors.toList()); 17 } 18 19 } • ParseException is checked, which breaks lambda expression Checked exceptions 
 & Java 8 Streams:
 Rough edges
  78. 78. 1 package com.example; 2 3 import java.text.SimpleDateFormat; 4 import java.util.Date; 5 import java.util.List; 6 import java.util.stream.Collectors; 7 8 class StreamsAndCheckedExceptions { 9 10 private static SimpleDateFormat sdf = 11 new SimpleDateFormat("ddMMyyyy"); 12 13 static List<Date> parseAll(List<String> strings) { 14 return strings.stream() 15 .map(sdf::parse) // won't compile! 16 .collect(Collectors.toList()); 17 } 18 19 } • ParseException is checked, which breaks lambda expression • solution using try-catch & rethrow-as-unchecked block is ugly and defeats the purpose of lambdas Checked exceptions 
 & Java 8 Streams:
 Rough edges
  79. 79. Checked exceptions 
 & Java 8 Streams:
 Try to the rescue 1 package com.example; 2 3 import javaslang.control.Try; 4 5 import java.text.SimpleDateFormat; 6 import java.util.Date; 7 import java.util.List; 8 import java.util.stream.Collectors; 9 10 class StreamsAndCheckedExceptions { 11 12 private static SimpleDateFormat sdf = 13 new SimpleDateFormat("ddMMyyyy"); 14 15 static List<Try<Date>> parseAll(List<String> strings) { 16 return strings.stream() 17 .map(s -> Try.of(() -> sdf.parse(s))) 18 .collect(Collectors.toList()); 19 } 20 21 } 1 package com.example; 2 3 import org.junit.Test; 4 5 import java.util.List; 6 7 import static java.lang.System.out; 8 import static java.util.Arrays.asList; 9 10 public class StreamsTest { 11 12 @Test 13 public void testNumberConversion() { 14 List<String> strings = asList("10042017", "23"); 15 out.println( 16 StreamsAndCheckedExceptions.parseAll(strings) 17 ); 18 19 // [ 20 // Success(Mon Apr 10 00:00:00 CEST 2017), 21 // Failure(ParseException: Unparseable date: "23") 22 // ] 23 } 24 25 }
  80. 80. • problem of checked exceptions solved
 Checked exceptions 
 & Java 8 Streams:
 Try to the rescue 1 package com.example; 2 3 import javaslang.control.Try; 4 5 import java.text.SimpleDateFormat; 6 import java.util.Date; 7 import java.util.List; 8 import java.util.stream.Collectors; 9 10 class StreamsAndCheckedExceptions { 11 12 private static SimpleDateFormat sdf = 13 new SimpleDateFormat("ddMMyyyy"); 14 15 static List<Try<Date>> parseAll(List<String> strings) { 16 return strings.stream() 17 .map(s -> Try.of(() -> sdf.parse(s))) 18 .collect(Collectors.toList()); 19 } 20 21 } 1 package com.example; 2 3 import org.junit.Test; 4 5 import java.util.List; 6 7 import static java.lang.System.out; 8 import static java.util.Arrays.asList; 9 10 public class StreamsTest { 11 12 @Test 13 public void testNumberConversion() { 14 List<String> strings = asList("10042017", "23"); 15 out.println( 16 StreamsAndCheckedExceptions.parseAll(strings) 17 ); 18 19 // [ 20 // Success(Mon Apr 10 00:00:00 CEST 2017), 21 // Failure(ParseException: Unparseable date: "23") 22 // ] 23 } 24 25 }
  81. 81. • problem of checked exceptions solved
 • we also gained explicit information of possibility of failure Checked exceptions 
 & Java 8 Streams:
 Try to the rescue 1 package com.example; 2 3 import javaslang.control.Try; 4 5 import java.text.SimpleDateFormat; 6 import java.util.Date; 7 import java.util.List; 8 import java.util.stream.Collectors; 9 10 class StreamsAndCheckedExceptions { 11 12 private static SimpleDateFormat sdf = 13 new SimpleDateFormat("ddMMyyyy"); 14 15 static List<Try<Date>> parseAll(List<String> strings) { 16 return strings.stream() 17 .map(s -> Try.of(() -> sdf.parse(s))) 18 .collect(Collectors.toList()); 19 } 20 21 } 1 package com.example; 2 3 import org.junit.Test; 4 5 import java.util.List; 6 7 import static java.lang.System.out; 8 import static java.util.Arrays.asList; 9 10 public class StreamsTest { 11 12 @Test 13 public void testNumberConversion() { 14 List<String> strings = asList("10042017", "23"); 15 out.println( 16 StreamsAndCheckedExceptions.parseAll(strings) 17 ); 18 19 // [ 20 // Success(Mon Apr 10 00:00:00 CEST 2017), 21 // Failure(ParseException: Unparseable date: "23") 22 // ] 23 } 24 25 }
  82. 82. Case for immutable data
  83. 83. Case for immutable data • Mutable state makes it significantly harder to reason about a system due to multiple code branches that can lead to mutation of state
  84. 84. Case for immutable data • Mutable state makes it significantly harder to reason about a system due to multiple code branches that can lead to mutation of state • Shared mutable state in concurrent environment requires synchronisation which is difficult and leads to subtle and 
 hard to replicate bugs
  85. 85. Case for immutable data • Mutable state makes it significantly harder to reason about a system due to multiple code branches that can lead to mutation of state • Shared mutable state in concurrent environment requires synchronisation which is difficult and leads to subtle and 
 hard to replicate bugs • Immutable objects and persistent data structures have none of those problems
  86. 86. Case for immutable data • Mutable state makes it significantly harder to reason about a system due to multiple code branches that can lead to mutation of state • Shared mutable state in concurrent environment requires synchronisation which is difficult and leads to subtle and 
 hard to replicate bugs • Immutable objects and persistent data structures have none of those problems • it's trivial to protect invariants once instance is immutable
  87. 87. Case for immutable data • Mutable state makes it significantly harder to reason about a system due to multiple code branches that can lead to mutation of state • Shared mutable state in concurrent environment requires synchronisation which is difficult and leads to subtle and 
 hard to replicate bugs • Immutable objects and persistent data structures have none of those problems • it's trivial to protect invariants once instance is immutable • no need for synchronisation ever
  88. 88. Case for immutable data • Mutable state makes it significantly harder to reason about a system due to multiple code branches that can lead to mutation of state • Shared mutable state in concurrent environment requires synchronisation which is difficult and leads to subtle and 
 hard to replicate bugs • Immutable objects and persistent data structures have none of those problems • it's trivial to protect invariants once instance is immutable • no need for synchronisation ever ... so how to do this in Java?
  89. 89. Immutable objects
 in plain Java 1 package com.example; 2 3 import java.util.Set; 4 5 public class Office { 6 7 private final Set<Room> rooms; 8 private final Set<Employee> employees; 9 10 public Office(Set<Room> rooms, Set<Employee> employees) { 11 this.rooms = rooms; 12 this.employees = employees; 13 } 14 15 public Set<Room> getRooms() { 16 return rooms; 17 } 18 19 public Set<Employee> getEmployees() { 20 return employees; 21 } 22 23 }
  90. 90. Immutable objects
 in plain Java • lots of getter noise1 package com.example; 2 3 import java.util.Set; 4 5 public class Office { 6 7 private final Set<Room> rooms; 8 private final Set<Employee> employees; 9 10 public Office(Set<Room> rooms, Set<Employee> employees) { 11 this.rooms = rooms; 12 this.employees = employees; 13 } 14 15 public Set<Room> getRooms() { 16 return rooms; 17 } 18 19 public Set<Employee> getEmployees() { 20 return employees; 21 } 22 23 }
  91. 91. Immutable objects
 in plain Java • lots of getter noise • if we want a builder, we have to create one and make constructor private 1 package com.example; 2 3 import java.util.Set; 4 5 public class Office { 6 7 private final Set<Room> rooms; 8 private final Set<Employee> employees; 9 10 public Office(Set<Room> rooms, Set<Employee> employees) { 11 this.rooms = rooms; 12 this.employees = employees; 13 } 14 15 public Set<Room> getRooms() { 16 return rooms; 17 } 18 19 public Set<Employee> getEmployees() { 20 return employees; 21 } 22 23 }
  92. 92. Immutable objects
 in plain Java • lots of getter noise • if we want a builder, we have to create one and make constructor private • optional field is painful as IDE will cry that fields should never have type Optional<T> 1 package com.example; 2 3 import java.util.Set; 4 5 public class Office { 6 7 private final Set<Room> rooms; 8 private final Set<Employee> employees; 9 10 public Office(Set<Room> rooms, Set<Employee> employees) { 11 this.rooms = rooms; 12 this.employees = employees; 13 } 14 15 public Set<Room> getRooms() { 16 return rooms; 17 } 18 19 public Set<Employee> getEmployees() { 20 return employees; 21 } 22 23 }
  93. 93. Immutable objects
 in plain Java • lots of getter noise • if we want a builder, we have to create one and make constructor private • optional field is painful as IDE will cry that fields should never have type Optional<T> • how can we get a copy of instance with only one field modified? 1 package com.example; 2 3 import java.util.Set; 4 5 public class Office { 6 7 private final Set<Room> rooms; 8 private final Set<Employee> employees; 9 10 public Office(Set<Room> rooms, Set<Employee> employees) { 11 this.rooms = rooms; 12 this.employees = employees; 13 } 14 15 public Set<Room> getRooms() { 16 return rooms; 17 } 18 19 public Set<Employee> getEmployees() { 20 return employees; 21 } 22 23 }
  94. 94. Enter Immutables 1 package com.example; 2 3 import javaslang.collection.Set; 4 import org.immutables.value.Value; 5 6 @Value.Immutable 7 @Value.Style(typeImmutable = "*") 8 abstract class AbstractOffice { 9 abstract Set<Room> rooms(); 10 11 abstract Set<Employee> employees(); 12 13 abstract boolean open(); 14 }
  95. 95. • that's all! Enter Immutables 1 package com.example; 2 3 import javaslang.collection.Set; 4 import org.immutables.value.Value; 5 6 @Value.Immutable 7 @Value.Style(typeImmutable = "*") 8 abstract class AbstractOffice { 9 abstract Set<Room> rooms(); 10 11 abstract Set<Employee> employees(); 12 13 abstract boolean open(); 14 }
  96. 96. • that's all! • highly customisable code generation Enter Immutables 1 package com.example; 2 3 import javaslang.collection.Set; 4 import org.immutables.value.Value; 5 6 @Value.Immutable 7 @Value.Style(typeImmutable = "*") 8 abstract class AbstractOffice { 9 abstract Set<Room> rooms(); 10 11 abstract Set<Employee> employees(); 12 13 abstract boolean open(); 14 }
  97. 97. • that's all! • highly customisable code generation • handles Optional<T> values correctly Enter Immutables 1 package com.example; 2 3 import javaslang.collection.Set; 4 import org.immutables.value.Value; 5 6 @Value.Immutable 7 @Value.Style(typeImmutable = "*") 8 abstract class AbstractOffice { 9 abstract Set<Room> rooms(); 10 11 abstract Set<Employee> employees(); 12 13 abstract boolean open(); 14 }
  98. 98. • that's all! • highly customisable code generation • handles Optional<T> values correctly • handles default values Enter Immutables 1 package com.example; 2 3 import javaslang.collection.Set; 4 import org.immutables.value.Value; 5 6 @Value.Immutable 7 @Value.Style(typeImmutable = "*") 8 abstract class AbstractOffice { 9 abstract Set<Room> rooms(); 10 11 abstract Set<Employee> employees(); 12 13 abstract boolean open(); 14 }
  99. 99. • that's all! • highly customisable code generation • handles Optional<T> values correctly • handles default values • Jackson / Gson integration out- of-the-box Enter Immutables 1 package com.example; 2 3 import javaslang.collection.Set; 4 import org.immutables.value.Value; 5 6 @Value.Immutable 7 @Value.Style(typeImmutable = "*") 8 abstract class AbstractOffice { 9 abstract Set<Room> rooms(); 10 11 abstract Set<Employee> employees(); 12 13 abstract boolean open(); 14 }
  100. 100. • that's all! • highly customisable code generation • handles Optional<T> values correctly • handles default values • Jackson / Gson integration out- of-the-box • provides with* methods allowing painless modification of immutable instances Enter Immutables 1 package com.example; 2 3 import javaslang.collection.Set; 4 import org.immutables.value.Value; 5 6 @Value.Immutable 7 @Value.Style(typeImmutable = "*") 8 abstract class AbstractOffice { 9 abstract Set<Room> rooms(); 10 11 abstract Set<Employee> employees(); 12 13 abstract boolean open(); 14 }
  101. 101. Immutables in action 1 package com.example; 2 3 import javaslang.collection.HashSet; 4 import org.junit.Test; 5 6 public class OfficeTest { 7 8 @Test 9 public void muchSafeSuchImmutableVeryWow() { 10 11 HashSet<Employee> employees = HashSet.of( 12 Employee.of("Boss") 13 ); 14 15 HashSet<Room> rooms = HashSet.of( 16 Room.of("Boss' office") 17 ); 18 19 Office office = Office.builder() 20 .employees(employees) 21 .rooms(rooms) 22 .open(true) 23 .build(); 24 25 System.out.println(office); 26 // Office{ 27 // rooms=HashSet(Room{name=Boss' office}), 28 // employees=HashSet(Employee{name=Boss}), 29 // open=true} 30 31 Office officeWithoutEmployees = office 32 .withEmployees(HashSet.empty()) 33 .withOpen(false); 34 35 System.out.println(officeWithoutEmployees); 36 // Office{ 37 // rooms=HashSet(Room{name=Boss' office}), 38 // employees=HashSet(), 39 // open=false} 40 } 41 }
  102. 102. • named factory method can be generated (of by default) Immutables in action 1 package com.example; 2 3 import javaslang.collection.HashSet; 4 import org.junit.Test; 5 6 public class OfficeTest { 7 8 @Test 9 public void muchSafeSuchImmutableVeryWow() { 10 11 HashSet<Employee> employees = HashSet.of( 12 Employee.of("Boss") 13 ); 14 15 HashSet<Room> rooms = HashSet.of( 16 Room.of("Boss' office") 17 ); 18 19 Office office = Office.builder() 20 .employees(employees) 21 .rooms(rooms) 22 .open(true) 23 .build(); 24 25 System.out.println(office); 26 // Office{ 27 // rooms=HashSet(Room{name=Boss' office}), 28 // employees=HashSet(Employee{name=Boss}), 29 // open=true} 30 31 Office officeWithoutEmployees = office 32 .withEmployees(HashSet.empty()) 33 .withOpen(false); 34 35 System.out.println(officeWithoutEmployees); 36 // Office{ 37 // rooms=HashSet(Room{name=Boss' office}), 38 // employees=HashSet(), 39 // open=false} 40 } 41 }
  103. 103. • named factory method can be generated (of by default) • builder class is generated by default Immutables in action 1 package com.example; 2 3 import javaslang.collection.HashSet; 4 import org.junit.Test; 5 6 public class OfficeTest { 7 8 @Test 9 public void muchSafeSuchImmutableVeryWow() { 10 11 HashSet<Employee> employees = HashSet.of( 12 Employee.of("Boss") 13 ); 14 15 HashSet<Room> rooms = HashSet.of( 16 Room.of("Boss' office") 17 ); 18 19 Office office = Office.builder() 20 .employees(employees) 21 .rooms(rooms) 22 .open(true) 23 .build(); 24 25 System.out.println(office); 26 // Office{ 27 // rooms=HashSet(Room{name=Boss' office}), 28 // employees=HashSet(Employee{name=Boss}), 29 // open=true} 30 31 Office officeWithoutEmployees = office 32 .withEmployees(HashSet.empty()) 33 .withOpen(false); 34 35 System.out.println(officeWithoutEmployees); 36 // Office{ 37 // rooms=HashSet(Room{name=Boss' office}), 38 // employees=HashSet(), 39 // open=false} 40 } 41 }
  104. 104. • named factory method can be generated (of by default) • builder class is generated by default • toString, hashCode and equals are generated using best practice methods Immutables in action 1 package com.example; 2 3 import javaslang.collection.HashSet; 4 import org.junit.Test; 5 6 public class OfficeTest { 7 8 @Test 9 public void muchSafeSuchImmutableVeryWow() { 10 11 HashSet<Employee> employees = HashSet.of( 12 Employee.of("Boss") 13 ); 14 15 HashSet<Room> rooms = HashSet.of( 16 Room.of("Boss' office") 17 ); 18 19 Office office = Office.builder() 20 .employees(employees) 21 .rooms(rooms) 22 .open(true) 23 .build(); 24 25 System.out.println(office); 26 // Office{ 27 // rooms=HashSet(Room{name=Boss' office}), 28 // employees=HashSet(Employee{name=Boss}), 29 // open=true} 30 31 Office officeWithoutEmployees = office 32 .withEmployees(HashSet.empty()) 33 .withOpen(false); 34 35 System.out.println(officeWithoutEmployees); 36 // Office{ 37 // rooms=HashSet(Room{name=Boss' office}), 38 // employees=HashSet(), 39 // open=false} 40 } 41 }
  105. 105. • named factory method can be generated (of by default) • builder class is generated by default • toString, hashCode and equals are generated using best practice methods • with(field) methods make obtaining modified instances a breeze! Immutables in action 1 package com.example; 2 3 import javaslang.collection.HashSet; 4 import org.junit.Test; 5 6 public class OfficeTest { 7 8 @Test 9 public void muchSafeSuchImmutableVeryWow() { 10 11 HashSet<Employee> employees = HashSet.of( 12 Employee.of("Boss") 13 ); 14 15 HashSet<Room> rooms = HashSet.of( 16 Room.of("Boss' office") 17 ); 18 19 Office office = Office.builder() 20 .employees(employees) 21 .rooms(rooms) 22 .open(true) 23 .build(); 24 25 System.out.println(office); 26 // Office{ 27 // rooms=HashSet(Room{name=Boss' office}), 28 // employees=HashSet(Employee{name=Boss}), 29 // open=true} 30 31 Office officeWithoutEmployees = office 32 .withEmployees(HashSet.empty()) 33 .withOpen(false); 34 35 System.out.println(officeWithoutEmployees); 36 // Office{ 37 // rooms=HashSet(Room{name=Boss' office}), 38 // employees=HashSet(), 39 // open=false} 40 } 41 }
  106. 106. • named factory method can be generated (of by default) • builder class is generated by default • toString, hashCode and equals are generated using best practice methods • with(field) methods make obtaining modified instances a breeze! • nulls are not allowed by default, so fail early rule is enforced Immutables in action 1 package com.example; 2 3 import javaslang.collection.HashSet; 4 import org.junit.Test; 5 6 public class OfficeTest { 7 8 @Test 9 public void muchSafeSuchImmutableVeryWow() { 10 11 HashSet<Employee> employees = HashSet.of( 12 Employee.of("Boss") 13 ); 14 15 HashSet<Room> rooms = HashSet.of( 16 Room.of("Boss' office") 17 ); 18 19 Office office = Office.builder() 20 .employees(employees) 21 .rooms(rooms) 22 .open(true) 23 .build(); 24 25 System.out.println(office); 26 // Office{ 27 // rooms=HashSet(Room{name=Boss' office}), 28 // employees=HashSet(Employee{name=Boss}), 29 // open=true} 30 31 Office officeWithoutEmployees = office 32 .withEmployees(HashSet.empty()) 33 .withOpen(false); 34 35 System.out.println(officeWithoutEmployees); 36 // Office{ 37 // rooms=HashSet(Room{name=Boss' office}), 38 // employees=HashSet(), 39 // open=false} 40 } 41 }
  107. 107. About that 
 javaslang.collection.HashSet
  108. 108. About that 
 javaslang.collection.HashSet • Javaslang provides immutable, persistent, purely functional collections that match Java Collections
  109. 109. About that 
 javaslang.collection.HashSet • Javaslang provides immutable, persistent, purely functional collections that match Java Collections • Immutable, so every operation returns a new instance
  110. 110. About that 
 javaslang.collection.HashSet • Javaslang provides immutable, persistent, purely functional collections that match Java Collections • Immutable, so every operation returns a new instance • Persistent, so creation of new instance means that data is shared between instances of collection and only the reference to modified element is replaced
  111. 111. About that 
 javaslang.collection.HashSet • Javaslang provides immutable, persistent, purely functional collections that match Java Collections • Immutable, so every operation returns a new instance • Persistent, so creation of new instance means that data is shared between instances of collection and only the reference to modified element is replaced • Functional, so all operations are referentially transparent
 (at least as long as values stored in collection are immutable!)
  112. 112. Javaslang 
 Collections APIs
  113. 113. • classic, imperative approach with StringBuilder: for loop and append calls Javaslang 
 Collections APIs
  114. 114. 1 String join(String... words) { 2 StringBuilder builder = new StringBuilder(); 3 for(String s : words) { 4 if (builder.length() > 0) { 5 builder.append(", "); 6 } 7 builder.append(s); 8 } 9 return builder.toString(); 10 } • classic, imperative approach with StringBuilder: for loop and append calls Javaslang 
 Collections APIs
  115. 115. 1 String join(String... words) { 2 StringBuilder builder = new StringBuilder(); 3 for(String s : words) { 4 if (builder.length() > 0) { 5 builder.append(", "); 6 } 7 builder.append(s); 8 } 9 return builder.toString(); 10 } • classic, imperative approach with StringBuilder: for loop and append calls • declarative use of List methods and fold (reduce to accumulator value) Javaslang 
 Collections APIs
  116. 116. 1 String join(String... words) { 2 StringBuilder builder = new StringBuilder(); 3 for(String s : words) { 4 if (builder.length() > 0) { 5 builder.append(", "); 6 } 7 builder.append(s); 8 } 9 return builder.toString(); 10 } 1 String join(String... words) { 2 return List.of(words) 3 .intersperse(", ") 4 .fold("", String::concat); 5 } • classic, imperative approach with StringBuilder: for loop and append calls • declarative use of List methods and fold (reduce to accumulator value) Javaslang 
 Collections APIs
  117. 117. 1 String join(String... words) { 2 StringBuilder builder = new StringBuilder(); 3 for(String s : words) { 4 if (builder.length() > 0) { 5 builder.append(", "); 6 } 7 builder.append(s); 8 } 9 return builder.toString(); 10 } 1 String join(String... words) { 2 return List.of(words) 3 .intersperse(", ") 4 .fold("", String::concat); 5 } • classic, imperative approach with StringBuilder: for loop and append calls • declarative use of List methods and fold (reduce to accumulator value) • a helper method! Javaslang 
 Collections APIs
  118. 118. 1 String join(String... words) { 2 StringBuilder builder = new StringBuilder(); 3 for(String s : words) { 4 if (builder.length() > 0) { 5 builder.append(", "); 6 } 7 builder.append(s); 8 } 9 return builder.toString(); 10 } 1 String join(String... words) { 2 return List.of(words) 3 .intersperse(", ") 4 .fold("", String::concat); 5 } 1 List.of(words).mkString(", "); • classic, imperative approach with StringBuilder: for loop and append calls • declarative use of List methods and fold (reduce to accumulator value) • a helper method! Javaslang 
 Collections APIs
  119. 119. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.collection.HashMap; 5 import javaslang.collection.HashSet; 6 import javaslang.collection.Map; 7 import javaslang.collection.Set; 8 9 public class PhoneNumberData { 10 11 public static final Set<String> phoneNumbers = 12 HashSet.of( 13 "+48413422345", 14 "413572456", 15 "+48413456990", 16 "48225697246", 17 "+48224914634", 18 "48126434972", 19 "+48128275242" 20 ); 21 22 public static final Map<String, String> areas = 23 HashMap.ofEntries( 24 Tuple.of("41", "Kielce"), 25 Tuple.of("22", "Warszawa"), 26 Tuple.of("12", "Kraków") 27 ); 28 29 } A more complex 
 example...
  120. 120. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.collection.HashMap; 5 import javaslang.collection.HashSet; 6 import javaslang.collection.Map; 7 import javaslang.collection.Set; 8 9 public class PhoneNumberData { 10 11 public static final Set<String> phoneNumbers = 12 HashSet.of( 13 "+48413422345", 14 "413572456", 15 "+48413456990", 16 "48225697246", 17 "+48224914634", 18 "48126434972", 19 "+48128275242" 20 ); 21 22 public static final Map<String, String> areas = 23 HashMap.ofEntries( 24 Tuple.of("41", "Kielce"), 25 Tuple.of("22", "Warszawa"), 26 Tuple.of("12", "Kraków") 27 ); 28 29 } • phoneNumbers: phone numbers in different formats A more complex 
 example...
  121. 121. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.collection.HashMap; 5 import javaslang.collection.HashSet; 6 import javaslang.collection.Map; 7 import javaslang.collection.Set; 8 9 public class PhoneNumberData { 10 11 public static final Set<String> phoneNumbers = 12 HashSet.of( 13 "+48413422345", 14 "413572456", 15 "+48413456990", 16 "48225697246", 17 "+48224914634", 18 "48126434972", 19 "+48128275242" 20 ); 21 22 public static final Map<String, String> areas = 23 HashMap.ofEntries( 24 Tuple.of("41", "Kielce"), 25 Tuple.of("22", "Warszawa"), 26 Tuple.of("12", "Kraków") 27 ); 28 29 } • phoneNumbers: phone numbers in different formats • areas: maps prefixes to area names A more complex 
 example...
  122. 122. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.collection.HashMap; 5 import javaslang.collection.HashSet; 6 import javaslang.collection.Map; 7 import javaslang.collection.Set; 8 9 public class PhoneNumberData { 10 11 public static final Set<String> phoneNumbers = 12 HashSet.of( 13 "+48413422345", 14 "413572456", 15 "+48413456990", 16 "48225697246", 17 "+48224914634", 18 "48126434972", 19 "+48128275242" 20 ); 21 22 public static final Map<String, String> areas = 23 HashMap.ofEntries( 24 Tuple.of("41", "Kielce"), 25 Tuple.of("22", "Warszawa"), 26 Tuple.of("12", "Kraków") 27 ); 28 29 } • phoneNumbers: phone numbers in different formats • areas: maps prefixes to area names • task: group numbers by area name A more complex 
 example...
  123. 123. Processing!1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import javaslang.collection.HashMap; 6 import javaslang.collection.HashSet; 7 import javaslang.collection.Map; 8 import javaslang.collection.Set; 9 import org.junit.Test; 10 11 import static java.util.function.Function.identity; 12 import static com.example.PhoneNumberData.*; 13 14 public class GroupNumbersTest { 15 16 @Test 17 public void processNumbers() { 18 Map<String, Set<String>> map = phoneNumbers 19 .filter(num -> num.length() >= 9) 20 .map(number -> Tuple.of(number, number)) 21 .map(tuple -> 22 tuple.map( 23 num -> num.replaceFirst("^+?48", ""), 24 identity() 25 ) 26 ) 27 .groupBy(tuple -> tuple._1().substring(0, 2)) 28 .bimap( 29 key -> areas.get(key).getOrElse("Nieznany"), 30 value -> value.map(Tuple2::_2) 31 ); 32 33 System.out.println(map); 34 } 35 36 }
  124. 124. Processing!1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import javaslang.collection.HashMap; 6 import javaslang.collection.HashSet; 7 import javaslang.collection.Map; 8 import javaslang.collection.Set; 9 import org.junit.Test; 10 11 import static java.util.function.Function.identity; 12 import static com.example.PhoneNumberData.*; 13 14 public class GroupNumbersTest { 15 16 @Test 17 public void processNumbers() { 18 Map<String, Set<String>> map = phoneNumbers 19 .filter(num -> num.length() >= 9) 20 .map(number -> Tuple.of(number, number)) 21 .map(tuple -> 22 tuple.map( 23 num -> num.replaceFirst("^+?48", ""), 24 identity() 25 ) 26 ) 27 .groupBy(tuple -> tuple._1().substring(0, 2)) 28 .bimap( 29 key -> areas.get(key).getOrElse("Nieznany"), 30 value -> value.map(Tuple2::_2) 31 ); 32 33 System.out.println(map); 34 } 35 36 }
  125. 125. Processing! • filter out numbers that are too short 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import javaslang.collection.HashMap; 6 import javaslang.collection.HashSet; 7 import javaslang.collection.Map; 8 import javaslang.collection.Set; 9 import org.junit.Test; 10 11 import static java.util.function.Function.identity; 12 import static com.example.PhoneNumberData.*; 13 14 public class GroupNumbersTest { 15 16 @Test 17 public void processNumbers() { 18 Map<String, Set<String>> map = phoneNumbers 19 .filter(num -> num.length() >= 9) 20 .map(number -> Tuple.of(number, number)) 21 .map(tuple -> 22 tuple.map( 23 num -> num.replaceFirst("^+?48", ""), 24 identity() 25 ) 26 ) 27 .groupBy(tuple -> tuple._1().substring(0, 2)) 28 .bimap( 29 key -> areas.get(key).getOrElse("Nieznany"), 30 value -> value.map(Tuple2::_2) 31 ); 32 33 System.out.println(map); 34 } 35 36 }
  126. 126. Processing! • filter out numbers that are too short 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import javaslang.collection.HashMap; 6 import javaslang.collection.HashSet; 7 import javaslang.collection.Map; 8 import javaslang.collection.Set; 9 import org.junit.Test; 10 11 import static java.util.function.Function.identity; 12 import static com.example.PhoneNumberData.*; 13 14 public class GroupNumbersTest { 15 16 @Test 17 public void processNumbers() { 18 Map<String, Set<String>> map = phoneNumbers 19 .filter(num -> num.length() >= 9) 20 .map(number -> Tuple.of(number, number)) 21 .map(tuple -> 22 tuple.map( 23 num -> num.replaceFirst("^+?48", ""), 24 identity() 25 ) 26 ) 27 .groupBy(tuple -> tuple._1().substring(0, 2)) 28 .bimap( 29 key -> areas.get(key).getOrElse("Nieznany"), 30 value -> value.map(Tuple2::_2) 31 ); 32 33 System.out.println(map); 34 } 35 36 }
  127. 127. Processing! • filter out numbers that are too short • we need to have original number and still process it - Tuples! 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import javaslang.collection.HashMap; 6 import javaslang.collection.HashSet; 7 import javaslang.collection.Map; 8 import javaslang.collection.Set; 9 import org.junit.Test; 10 11 import static java.util.function.Function.identity; 12 import static com.example.PhoneNumberData.*; 13 14 public class GroupNumbersTest { 15 16 @Test 17 public void processNumbers() { 18 Map<String, Set<String>> map = phoneNumbers 19 .filter(num -> num.length() >= 9) 20 .map(number -> Tuple.of(number, number)) 21 .map(tuple -> 22 tuple.map( 23 num -> num.replaceFirst("^+?48", ""), 24 identity() 25 ) 26 ) 27 .groupBy(tuple -> tuple._1().substring(0, 2)) 28 .bimap( 29 key -> areas.get(key).getOrElse("Nieznany"), 30 value -> value.map(Tuple2::_2) 31 ); 32 33 System.out.println(map); 34 } 35 36 }
  128. 128. Processing! • filter out numbers that are too short • we need to have original number and still process it - Tuples! 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import javaslang.collection.HashMap; 6 import javaslang.collection.HashSet; 7 import javaslang.collection.Map; 8 import javaslang.collection.Set; 9 import org.junit.Test; 10 11 import static java.util.function.Function.identity; 12 import static com.example.PhoneNumberData.*; 13 14 public class GroupNumbersTest { 15 16 @Test 17 public void processNumbers() { 18 Map<String, Set<String>> map = phoneNumbers 19 .filter(num -> num.length() >= 9) 20 .map(number -> Tuple.of(number, number)) 21 .map(tuple -> 22 tuple.map( 23 num -> num.replaceFirst("^+?48", ""), 24 identity() 25 ) 26 ) 27 .groupBy(tuple -> tuple._1().substring(0, 2)) 28 .bimap( 29 key -> areas.get(key).getOrElse("Nieznany"), 30 value -> value.map(Tuple2::_2) 31 ); 32 33 System.out.println(map); 34 } 35 36 }
  129. 129. Processing! • filter out numbers that are too short • we need to have original number and still process it - Tuples! • strip international code from processed tuple entry, don't do anything to original 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import javaslang.collection.HashMap; 6 import javaslang.collection.HashSet; 7 import javaslang.collection.Map; 8 import javaslang.collection.Set; 9 import org.junit.Test; 10 11 import static java.util.function.Function.identity; 12 import static com.example.PhoneNumberData.*; 13 14 public class GroupNumbersTest { 15 16 @Test 17 public void processNumbers() { 18 Map<String, Set<String>> map = phoneNumbers 19 .filter(num -> num.length() >= 9) 20 .map(number -> Tuple.of(number, number)) 21 .map(tuple -> 22 tuple.map( 23 num -> num.replaceFirst("^+?48", ""), 24 identity() 25 ) 26 ) 27 .groupBy(tuple -> tuple._1().substring(0, 2)) 28 .bimap( 29 key -> areas.get(key).getOrElse("Nieznany"), 30 value -> value.map(Tuple2::_2) 31 ); 32 33 System.out.println(map); 34 } 35 36 }
  130. 130. Processing! • filter out numbers that are too short • we need to have original number and still process it - Tuples! • strip international code from processed tuple entry, don't do anything to original 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import javaslang.collection.HashMap; 6 import javaslang.collection.HashSet; 7 import javaslang.collection.Map; 8 import javaslang.collection.Set; 9 import org.junit.Test; 10 11 import static java.util.function.Function.identity; 12 import static com.example.PhoneNumberData.*; 13 14 public class GroupNumbersTest { 15 16 @Test 17 public void processNumbers() { 18 Map<String, Set<String>> map = phoneNumbers 19 .filter(num -> num.length() >= 9) 20 .map(number -> Tuple.of(number, number)) 21 .map(tuple -> 22 tuple.map( 23 num -> num.replaceFirst("^+?48", ""), 24 identity() 25 ) 26 ) 27 .groupBy(tuple -> tuple._1().substring(0, 2)) 28 .bimap( 29 key -> areas.get(key).getOrElse("Nieznany"), 30 value -> value.map(Tuple2::_2) 31 ); 32 33 System.out.println(map); 34 } 35 36 }
  131. 131. Processing! • filter out numbers that are too short • we need to have original number and still process it - Tuples! • strip international code from processed tuple entry, don't do anything to original • group tuples by area code obtained from processed tuple entry 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import javaslang.collection.HashMap; 6 import javaslang.collection.HashSet; 7 import javaslang.collection.Map; 8 import javaslang.collection.Set; 9 import org.junit.Test; 10 11 import static java.util.function.Function.identity; 12 import static com.example.PhoneNumberData.*; 13 14 public class GroupNumbersTest { 15 16 @Test 17 public void processNumbers() { 18 Map<String, Set<String>> map = phoneNumbers 19 .filter(num -> num.length() >= 9) 20 .map(number -> Tuple.of(number, number)) 21 .map(tuple -> 22 tuple.map( 23 num -> num.replaceFirst("^+?48", ""), 24 identity() 25 ) 26 ) 27 .groupBy(tuple -> tuple._1().substring(0, 2)) 28 .bimap( 29 key -> areas.get(key).getOrElse("Nieznany"), 30 value -> value.map(Tuple2::_2) 31 ); 32 33 System.out.println(map); 34 } 35 36 }
  132. 132. Processing! • filter out numbers that are too short • we need to have original number and still process it - Tuples! • strip international code from processed tuple entry, don't do anything to original • group tuples by area code obtained from processed tuple entry 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import javaslang.collection.HashMap; 6 import javaslang.collection.HashSet; 7 import javaslang.collection.Map; 8 import javaslang.collection.Set; 9 import org.junit.Test; 10 11 import static java.util.function.Function.identity; 12 import static com.example.PhoneNumberData.*; 13 14 public class GroupNumbersTest { 15 16 @Test 17 public void processNumbers() { 18 Map<String, Set<String>> map = phoneNumbers 19 .filter(num -> num.length() >= 9) 20 .map(number -> Tuple.of(number, number)) 21 .map(tuple -> 22 tuple.map( 23 num -> num.replaceFirst("^+?48", ""), 24 identity() 25 ) 26 ) 27 .groupBy(tuple -> tuple._1().substring(0, 2)) 28 .bimap( 29 key -> areas.get(key).getOrElse("Nieznany"), 30 value -> value.map(Tuple2::_2) 31 ); 32 33 System.out.println(map); 34 } 35 36 }
  133. 133. Processing! • filter out numbers that are too short • we need to have original number and still process it - Tuples! • strip international code from processed tuple entry, don't do anything to original • group tuples by area code obtained from processed tuple entry • map keys and values to either area name and set of original phone numbers 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import javaslang.collection.HashMap; 6 import javaslang.collection.HashSet; 7 import javaslang.collection.Map; 8 import javaslang.collection.Set; 9 import org.junit.Test; 10 11 import static java.util.function.Function.identity; 12 import static com.example.PhoneNumberData.*; 13 14 public class GroupNumbersTest { 15 16 @Test 17 public void processNumbers() { 18 Map<String, Set<String>> map = phoneNumbers 19 .filter(num -> num.length() >= 9) 20 .map(number -> Tuple.of(number, number)) 21 .map(tuple -> 22 tuple.map( 23 num -> num.replaceFirst("^+?48", ""), 24 identity() 25 ) 26 ) 27 .groupBy(tuple -> tuple._1().substring(0, 2)) 28 .bimap( 29 key -> areas.get(key).getOrElse("Nieznany"), 30 value -> value.map(Tuple2::_2) 31 ); 32 33 System.out.println(map); 34 } 35 36 }
  134. 134. Results:
  135. 135. Results: HashMap(
 (Kielce, HashSet(+48413456990, +48413422345, 413572456)), 
 (Kraków, HashSet(+48128275242, 48126434972)), 
 (Warszawa, HashSet(+48224914634, 48225697246))
 )
  136. 136. Results: HashMap(
 (Kielce, HashSet(+48413456990, +48413422345, 413572456)), 
 (Kraków, HashSet(+48128275242, 48126434972)), 
 (Warszawa, HashSet(+48224914634, 48225697246))
 ) ... neat!

  137. 137. Results: HashMap(
 (Kielce, HashSet(+48413456990, +48413422345, 413572456)), 
 (Kraków, HashSet(+48128275242, 48126434972)), 
 (Warszawa, HashSet(+48224914634, 48225697246))
 ) ... neat!
 But what if that was an infinite stream of phone numbers?
  138. 138. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.collection.HashMap; 5 import javaslang.collection.Map; 6 import rx.Observable; 7 8 public class StreamingPhoneNumberData { 9 10 public static final Observable<String> phoneNumbers = 11 Observable.from(new String[]{ 12 "+48413422345", 13 "413572456", 14 "+48413456990", 15 "48225697246", 16 "+48224914634", 17 "48126434972", 18 "+48128275242" 19 }); 20 21 public static final Map<String, String> areas = 22 HashMap.ofEntries( 23 Tuple.of("41", "Kielce"), 24 Tuple.of("22", "Warszawa"), 25 Tuple.of("12", "Kraków") 26 ); 27 28 } Event streams 
 with RxJava
  139. 139. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.collection.HashMap; 5 import javaslang.collection.Map; 6 import rx.Observable; 7 8 public class StreamingPhoneNumberData { 9 10 public static final Observable<String> phoneNumbers = 11 Observable.from(new String[]{ 12 "+48413422345", 13 "413572456", 14 "+48413456990", 15 "48225697246", 16 "+48224914634", 17 "48126434972", 18 "+48128275242" 19 }); 20 21 public static final Map<String, String> areas = 22 HashMap.ofEntries( 23 Tuple.of("41", "Kielce"), 24 Tuple.of("22", "Warszawa"), 25 Tuple.of("12", "Kraków") 26 ); 27 28 } Event streams 
 with RxJava
  140. 140. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.collection.HashMap; 5 import javaslang.collection.Map; 6 import rx.Observable; 7 8 public class StreamingPhoneNumberData { 9 10 public static final Observable<String> phoneNumbers = 11 Observable.from(new String[]{ 12 "+48413422345", 13 "413572456", 14 "+48413456990", 15 "48225697246", 16 "+48224914634", 17 "48126434972", 18 "+48128275242" 19 }); 20 21 public static final Map<String, String> areas = 22 HashMap.ofEntries( 23 Tuple.of("41", "Kielce"), 24 Tuple.of("22", "Warszawa"), 25 Tuple.of("12", "Kraków") 26 ); 27 28 } Event streams 
 with RxJava • Observable<T> is 
 a 0 .. n collection of events (potentially infinite!)
  141. 141. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import org.junit.Test; 6 import rx.observables.GroupedObservable; 7 8 import static com.example.PhoneNumberData.areas; 9 import static com.example.StreamingData.phoneNumbers; 10 import static java.lang.System.out; 11 import static java.util.function.Function.identity; 12 13 public class RxGroupPhoneNumbersTest { 14 15 @Test 16 public void processNumbers() { 17 phoneNumbers 18 .filter(num -> num.length() >= 9) 19 .map(number -> Tuple.of(number, number)) 20 .map(tuple -> 21 tuple.map( 22 num -> num.replaceFirst("^+?48", ""), 23 identity() 24 ) 25 ) 26 .groupBy(tuple -> tuple._1().substring(0, 2)) 27 .map(grouped -> 28 GroupedObservable.from( 29 areas.get(grouped.getKey()) 30 .getOrElse("Unknown"), 31 grouped.map(Tuple2::_2) 32 ) 33 ) 34 .forEach(grouped -> grouped.forEach(number -> 35 out.println( 36 grouped.getKey() + ": " + number 37 ) 38 ) 39 ); 40 } 41 42 } Event streams 
 with RxJava: 
 reuse your lambdas!
  142. 142. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import org.junit.Test; 6 import rx.observables.GroupedObservable; 7 8 import static com.example.PhoneNumberData.areas; 9 import static com.example.StreamingData.phoneNumbers; 10 import static java.lang.System.out; 11 import static java.util.function.Function.identity; 12 13 public class RxGroupPhoneNumbersTest { 14 15 @Test 16 public void processNumbers() { 17 phoneNumbers 18 .filter(num -> num.length() >= 9) 19 .map(number -> Tuple.of(number, number)) 20 .map(tuple -> 21 tuple.map( 22 num -> num.replaceFirst("^+?48", ""), 23 identity() 24 ) 25 ) 26 .groupBy(tuple -> tuple._1().substring(0, 2)) 27 .map(grouped -> 28 GroupedObservable.from( 29 areas.get(grouped.getKey()) 30 .getOrElse("Unknown"), 31 grouped.map(Tuple2::_2) 32 ) 33 ) 34 .forEach(grouped -> grouped.forEach(number -> 35 out.println( 36 grouped.getKey() + ": " + number 37 ) 38 ) 39 ); 40 } 41 42 } Event streams 
 with RxJava: 
 reuse your lambdas!
  143. 143. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import org.junit.Test; 6 import rx.observables.GroupedObservable; 7 8 import static com.example.PhoneNumberData.areas; 9 import static com.example.StreamingData.phoneNumbers; 10 import static java.lang.System.out; 11 import static java.util.function.Function.identity; 12 13 public class RxGroupPhoneNumbersTest { 14 15 @Test 16 public void processNumbers() { 17 phoneNumbers 18 .filter(num -> num.length() >= 9) 19 .map(number -> Tuple.of(number, number)) 20 .map(tuple -> 21 tuple.map( 22 num -> num.replaceFirst("^+?48", ""), 23 identity() 24 ) 25 ) 26 .groupBy(tuple -> tuple._1().substring(0, 2)) 27 .map(grouped -> 28 GroupedObservable.from( 29 areas.get(grouped.getKey()) 30 .getOrElse("Unknown"), 31 grouped.map(Tuple2::_2) 32 ) 33 ) 34 .forEach(grouped -> grouped.forEach(number -> 35 out.println( 36 grouped.getKey() + ": " + number 37 ) 38 ) 39 ); 40 } 41 42 } Event streams 
 with RxJava: 
 reuse your lambdas! • highlighted code is the same as code used on Javaslang collection!
  144. 144. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import org.junit.Test; 6 import rx.observables.GroupedObservable; 7 8 import static com.example.PhoneNumberData.areas; 9 import static com.example.StreamingData.phoneNumbers; 10 import static java.lang.System.out; 11 import static java.util.function.Function.identity; 12 13 public class RxGroupPhoneNumbersTest { 14 15 @Test 16 public void processNumbers() { 17 phoneNumbers 18 .filter(num -> num.length() >= 9) 19 .map(number -> Tuple.of(number, number)) 20 .map(tuple -> 21 tuple.map( 22 num -> num.replaceFirst("^+?48", ""), 23 identity() 24 ) 25 ) 26 .groupBy(tuple -> tuple._1().substring(0, 2)) 27 .map(grouped -> 28 GroupedObservable.from( 29 areas.get(grouped.getKey()) 30 .getOrElse("Unknown"), 31 grouped.map(Tuple2::_2) 32 ) 33 ) 34 .forEach(grouped -> grouped.forEach(number -> 35 out.println( 36 grouped.getKey() + ": " + number 37 ) 38 ) 39 ); 40 } 41 42 } Event streams 
 with RxJava: 
 reuse your lambdas! • highlighted code is the same as code used on Javaslang collection! • we are operating on potentially infinite stream, so groupBy returns Observable of GroupedObservables - a stream of streams of values grouped by key!
  145. 145. 1 package com.example; 2 3 import javaslang.Tuple; 4 import javaslang.Tuple2; 5 import org.junit.Test; 6 import rx.observables.GroupedObservable; 7 8 import static com.example.PhoneNumberData.areas; 9 import static com.example.StreamingData.phoneNumbers; 10 import static java.lang.System.out; 11 import static java.util.function.Function.identity; 12 13 public class RxGroupPhoneNumbersTest { 14 15 @Test 16 public void processNumbers() { 17 phoneNumbers 18 .filter(num -> num.length() >= 9) 19 .map(number -> Tuple.of(number, number)) 20 .map(tuple -> 21 tuple.map( 22 num -> num.replaceFirst("^+?48", ""), 23 identity() 24 ) 25 ) 26 .groupBy(tuple -> tuple._1().substring(0, 2)) 27 .map(grouped -> 28 GroupedObservable.from( 29 areas.get(grouped.getKey()) 30 .getOrElse("Unknown"), 31 grouped.map(Tuple2::_2) 32 ) 33 ) 34 .forEach(grouped -> grouped.forEach(number -> 35 out.println( 36 grouped.getKey() + ": " + number 37 ) 38 ) 39 ); 40 } 41 42 } Event streams 
 with RxJava: 
 reuse your lambdas! • highlighted code is the same as code used on Javaslang collection! • we are operating on potentially infinite stream, so groupBy returns Observable of GroupedObservables - a stream of streams of values grouped by key!
  146. 146. Use cases of
 Reactive Extensions
  147. 147. Use cases of
 Reactive Extensions • Streams of events
  148. 148. Use cases of
 Reactive Extensions • Streams of events • Parallel processing
  149. 149. Use cases of
 Reactive Extensions • Streams of events • Parallel processing • Asynchronous programming
  150. 150. Use cases of
 Reactive Extensions • Streams of events • Parallel processing • Asynchronous programming • Bonus: functional error handling
  151. 151. Use cases of
 Reactive Extensions • Streams of events • Parallel processing • Asynchronous programming • Bonus: functional error handling • Bonus: reactive streams specification
  152. 152. Functional Programming in Java: not needed, right?
  153. 153. Functional Programming in Java: not needed, right? • Microservices need linear scalability on instance level, which is hard to achieve using thread-per-request model, asynchronous frameworks offer this 
 out-of-the-box
  154. 154. Functional Programming in Java: not needed, right? • Microservices need linear scalability on instance level, which is hard to achieve using thread-per-request model, asynchronous frameworks offer this 
 out-of-the-box • Reactive, event-driven programming model has already been included into latest Java version - reactive streams interfaces made it to JDK9!
  155. 155. Also - Spring 5: 1 @GetMapping("/accounts/{id}/alerts") 2 public Flux<Alert> getAccountAlerts(@PathVariable Long id) { 3 4 return this.repository.getAccount(id) 5 .flatMap(account -> 6 this.webClient 7 .perform(get("/alerts/{key}", account.getKey())) 8 .extract(bodyStream(Alert.class)) 9 ); 10 }
  156. 156. And Vert.x: 1 server.requestStream() 2 .toObservable() 3 .subscribe(request -> 4 request.toObservable() 5 .lift(RxHelper.unmarshaller(MyPojo.class)) 6 .map(Marshaller::toMongoDocument) 7 .flatMap(jsonObject -> mongo.insertObservable(MY_POJOS, jsonObject)) 8 .subscribe( 9 () -> request.response() 10 .setStatusCode(200) 11 .end(), 12 err -> request.response() 13 .setStatusCode(500) 14 .end() 15 ) 16 );
  157. 157. Thanks for listening!

×