157
Łukasz Biały Functional Java 8 Introduction Scala / Java Developer [email protected]

Functional Java 8 - Introduction

Embed Size (px)

Citation preview

Page 1: Functional Java 8 - Introduction

ŁukaszBiały

Functional Java 8

IntroductionScala / Java [email protected]

Page 2: Functional Java 8 - Introduction

What is functional programming?

Page 3: Functional Java 8 - Introduction

What is functional programming?• usage of pure functions

Page 4: Functional Java 8 - Introduction

What is functional programming?• usage of pure functions

• expressions instead of statements

Page 5: Functional Java 8 - Introduction

What is functional programming?• usage of pure functions

• expressions instead of statements

• widespread immutability

Page 6: Functional Java 8 - Introduction

What is functional programming?• usage of pure functions

• expressions instead of statements

• widespread immutability

• referential transparency

Page 7: Functional Java 8 - Introduction

What is functional programming?• usage of pure functions

• expressions instead of statements

• widespread immutability

• referential transparency

• lazy evaluation

Page 8: Functional Java 8 - Introduction

What is functional programming?• usage of pure functions

• expressions instead of statements

• widespread immutability

• referential transparency

• lazy evaluation

... and functional idioms!

Page 9: Functional Java 8 - Introduction

What's the gain?

Page 10: Functional Java 8 - Introduction

What's the gain?

• More predictable execution (same arguments always yield the same results)

Page 11: Functional Java 8 - Introduction

What's the gain?

• More predictable execution (same arguments always yield the same results)

• Easier to reason about

Page 12: Functional Java 8 - Introduction

What's the gain?

• More predictable execution (same arguments always yield the same results)

• Easier to reason about

• Easier to enforce strict type correctness

Page 13: Functional Java 8 - Introduction

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

Page 14: Functional Java 8 - Introduction

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

Page 15: Functional Java 8 - Introduction

How does this relate to Java at all?

Page 16: Functional Java 8 - Introduction

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) ...

Page 17: Functional Java 8 - Introduction

Most common errors in Java

Page 18: Functional Java 8 - Introduction

Most common errors in Java

• Takipi monitoring service analysed error logs of over 1k enterprise apps

Page 19: Functional Java 8 - Introduction

Most common errors in Java

• Takipi monitoring service analysed error logs of over 1k enterprise apps

• 29,965,285 exceptions over 30 days

Page 20: Functional Java 8 - Introduction

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

Page 21: Functional Java 8 - Introduction

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

Page 22: Functional Java 8 - Introduction

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/

Page 23: Functional Java 8 - Introduction

http://blog.takipi.com/the-top-10-exceptions-types-in-production-java-applications-based-on-1b-events/

Page 24: Functional Java 8 - Introduction

How can FP help?

Page 25: Functional Java 8 - Introduction

How can FP help?

• Functional style makes implicit rules governing code explicit, often as a part of type signatures

Page 26: Functional Java 8 - Introduction

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

Page 27: Functional Java 8 - Introduction

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

Page 28: Functional Java 8 - Introduction

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 :)

Page 29: Functional Java 8 - Introduction

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 }

Optionalityof values

Page 30: Functional Java 8 - Introduction

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 }

Optionalityof values

• optionality of value is not explicitly known from method signature

Page 31: Functional Java 8 - Introduction

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 }

Optionalityof values

• optionality of value is not explicitly known from method signature

Page 32: Functional Java 8 - Introduction

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 }

Optionalityof values

• optionality of value is not explicitly known from method signature

Page 33: Functional Java 8 - Introduction

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 }

Optionalityof values

• optionality of value is not explicitly known from method signature

• not feeling especially bright today? have a NullPointerException in runtime

Page 34: Functional Java 8 - Introduction

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 }

Optionalityof values

• optionality of value is not explicitly known from method signature

• not feeling especially bright today? have a NullPointerException in runtime

Page 35: Functional Java 8 - Introduction

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 }

Optionalityof values

• optionality of value is not explicitly known from method signature

• not feeling especially bright today? have a NullPointerException in runtime

Page 36: Functional Java 8 - Introduction

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 }

Optionalityof 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

Page 37: Functional Java 8 - Introduction

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 }

Optionalityof 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!

Page 38: Functional Java 8 - Introduction

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 }

Page 39: Functional Java 8 - Introduction

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

Page 40: Functional Java 8 - Introduction

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)

Page 41: Functional Java 8 - Introduction

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)

Page 42: Functional Java 8 - Introduction

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

Page 43: Functional Java 8 - Introduction

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

Page 44: Functional Java 8 - Introduction

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

Page 45: Functional Java 8 - Introduction

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

Page 46: Functional Java 8 - Introduction

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>

Page 47: Functional Java 8 - Introduction

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>

Page 48: Functional Java 8 - Introduction

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>

Page 49: Functional Java 8 - Introduction

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 }

Page 50: Functional Java 8 - Introduction

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 :(

Page 51: Functional Java 8 - Introduction

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

Page 52: Functional Java 8 - Introduction

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> fromfirstCharacter call

Page 53: Functional Java 8 - Introduction

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> fromfirstCharacter call

• how to extract value in safe manner?

Page 54: Functional Java 8 - Introduction

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

Page 55: Functional Java 8 - Introduction

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

Page 56: Functional Java 8 - Introduction

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

Page 57: Functional Java 8 - Introduction

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

Page 58: Functional Java 8 - Introduction

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 }

Page 59: Functional Java 8 - Introduction

• 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 }

Page 60: Functional Java 8 - Introduction

• 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 }

Page 61: Functional Java 8 - Introduction

• 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 }

Page 62: Functional Java 8 - Introduction

• 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 }

Page 63: Functional Java 8 - Introduction

• 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 }

Page 64: Functional Java 8 - Introduction

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 APIrevisited: 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 }

Page 65: Functional Java 8 - Introduction

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 APIrevisited: 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 }

Page 66: Functional Java 8 - Introduction

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 APIrevisited: 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 }

Page 67: Functional Java 8 - Introduction

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 APIrevisited: 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 }

Page 68: Functional Java 8 - Introduction

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

Page 69: Functional Java 8 - Introduction

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

Page 70: Functional Java 8 - Introduction

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

Page 71: Functional Java 8 - Introduction

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

Page 72: Functional Java 8 - Introduction

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 }

Page 73: Functional Java 8 - Introduction

• 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 }

Page 74: Functional Java 8 - Introduction

• 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 }

Page 75: Functional Java 8 - Introduction

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

Page 76: Functional Java 8 - Introduction

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

Page 77: Functional Java 8 - Introduction

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

Page 78: Functional Java 8 - Introduction

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

Page 79: Functional Java 8 - Introduction

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 }

Page 80: Functional Java 8 - Introduction

• 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 }

Page 81: Functional Java 8 - Introduction

• 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 }

Page 82: Functional Java 8 - Introduction

Case for immutable data

Page 83: Functional Java 8 - Introduction

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

Page 84: Functional Java 8 - Introduction

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

Page 85: Functional Java 8 - Introduction

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

Page 86: Functional Java 8 - Introduction

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

Page 87: Functional Java 8 - Introduction

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

Page 88: Functional Java 8 - Introduction

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?

Page 89: Functional Java 8 - Introduction

Immutable objectsin 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 }

Page 90: Functional Java 8 - Introduction

Immutable objectsin plain Java

• lots of getter noise 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 }

Page 91: Functional Java 8 - Introduction

Immutable objectsin 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 }

Page 92: Functional Java 8 - Introduction

Immutable objectsin 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 }

Page 93: Functional Java 8 - Introduction

Immutable objectsin 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 }

Page 94: Functional Java 8 - Introduction

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 }

Page 95: Functional Java 8 - Introduction

• 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 }

Page 96: Functional Java 8 - Introduction

• 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 }

Page 97: Functional Java 8 - Introduction

• 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 }

Page 98: Functional Java 8 - Introduction

• 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 }

Page 99: Functional Java 8 - Introduction

• 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 }

Page 100: Functional Java 8 - Introduction

• 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 }

Page 101: Functional Java 8 - Introduction

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 }

Page 102: Functional Java 8 - Introduction

• 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 }

Page 103: Functional Java 8 - Introduction

• 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 }

Page 104: Functional Java 8 - Introduction

• 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 }

Page 105: Functional Java 8 - Introduction

• 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 }

Page 106: Functional Java 8 - Introduction

• 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 }

Page 107: Functional Java 8 - Introduction

About that javaslang.collection.HashSet

Page 108: Functional Java 8 - Introduction

About that javaslang.collection.HashSet

• Javaslang provides immutable, persistent, purely functional collections that match Java Collections

Page 109: Functional Java 8 - Introduction

About that javaslang.collection.HashSet

• Javaslang provides immutable, persistent, purely functional collections that match Java Collections

• Immutable, so every operation returns a new instance

Page 110: Functional Java 8 - Introduction

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

Page 111: Functional Java 8 - Introduction

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!)

Page 112: Functional Java 8 - Introduction

Javaslang Collections APIs

Page 113: Functional Java 8 - Introduction

• classic, imperative approach with StringBuilder: for loop and append calls

Javaslang Collections APIs

Page 114: Functional Java 8 - Introduction

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

Page 115: Functional Java 8 - Introduction

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

Page 116: Functional Java 8 - Introduction

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

Page 117: Functional Java 8 - Introduction

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

Page 118: Functional Java 8 - Introduction

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

Page 119: Functional Java 8 - Introduction

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...

Page 120: Functional Java 8 - Introduction

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...

Page 121: Functional Java 8 - Introduction

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...

Page 122: Functional Java 8 - Introduction

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...

Page 123: Functional Java 8 - Introduction

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 }

Page 124: Functional Java 8 - Introduction

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 }

Page 125: Functional Java 8 - Introduction

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 }

Page 126: Functional Java 8 - Introduction

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 }

Page 127: Functional Java 8 - Introduction

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 }

Page 128: Functional Java 8 - Introduction

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 }

Page 129: Functional Java 8 - Introduction

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 }

Page 130: Functional Java 8 - Introduction

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 }

Page 131: Functional Java 8 - Introduction

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 }

Page 132: Functional Java 8 - Introduction

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 }

Page 133: Functional Java 8 - Introduction

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 }

Page 134: Functional Java 8 - Introduction

Results:

Page 135: Functional Java 8 - Introduction

Results:HashMap( (Kielce, HashSet(+48413456990, +48413422345, 413572456)), (Kraków, HashSet(+48128275242, 48126434972)), (Warszawa, HashSet(+48224914634, 48225697246)) )

Page 136: Functional Java 8 - Introduction

Results:HashMap( (Kielce, HashSet(+48413456990, +48413422345, 413572456)), (Kraków, HashSet(+48128275242, 48126434972)), (Warszawa, HashSet(+48224914634, 48225697246)) )

... neat!

Page 137: Functional Java 8 - Introduction

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?

Page 138: Functional Java 8 - Introduction

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

Page 139: Functional Java 8 - Introduction

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

Page 140: Functional Java 8 - Introduction

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!)

Page 141: Functional Java 8 - Introduction

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!

Page 142: Functional Java 8 - Introduction

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!

Page 143: Functional Java 8 - Introduction

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!

Page 144: Functional Java 8 - Introduction

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!

Page 145: Functional Java 8 - Introduction

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!

Page 146: Functional Java 8 - Introduction

Use cases ofReactive Extensions

Page 147: Functional Java 8 - Introduction

Use cases ofReactive Extensions

• Streams of events

Page 148: Functional Java 8 - Introduction

Use cases ofReactive Extensions

• Streams of events

• Parallel processing

Page 149: Functional Java 8 - Introduction

Use cases ofReactive Extensions

• Streams of events

• Parallel processing

• Asynchronous programming

Page 150: Functional Java 8 - Introduction

Use cases ofReactive Extensions

• Streams of events

• Parallel processing

• Asynchronous programming

• Bonus: functional error handling

Page 151: Functional Java 8 - Introduction

Use cases ofReactive Extensions

• Streams of events

• Parallel processing

• Asynchronous programming

• Bonus: functional error handling

• Bonus: reactive streams specification

Page 152: Functional Java 8 - Introduction

Functional Programming in Java: not needed, right?

Page 153: Functional Java 8 - Introduction

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

Page 154: Functional Java 8 - Introduction

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!

Page 155: Functional Java 8 - Introduction

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 }

Page 156: Functional Java 8 - Introduction

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 );

Page 157: Functional Java 8 - Introduction

Thanks for listening!