├── .gitignore ├── src ├── main │ └── java │ │ └── de │ │ └── stevenschwenke │ │ └── java │ │ └── java8workshop │ │ ├── FunctionalInterfaceGen2.java │ │ ├── ChangeLog.java │ │ ├── DefaultMethodImplementingEmptyClass.java │ │ ├── SlightlyMoreSophisticatedFunctionalInterface.java │ │ ├── DeepThought.java │ │ ├── DefaultMethodSame1.java │ │ ├── DefaultMethodSame2.java │ │ ├── InterfaceWithStaticMethod.java │ │ ├── Change.java │ │ ├── DefaultMethodOverridingClass.java │ │ ├── InterfaceWithDefaultMethodGen2.java │ │ ├── InterfaceWithDefaultMethodGen1.java │ │ ├── InterfaceWithDefaultMethod.java │ │ ├── FunctionalInterfaceGen1.java │ │ ├── HighlySophisticatedFunctionalInterface.java │ │ ├── SimpleFunctionalInterface.java │ │ └── JavaVersion.java └── test │ └── java │ └── de │ └── stevenschwenke │ └── java │ └── java8workshop │ ├── C_03_MethodReferences_Exercises.java │ ├── C_12_Outlook.java │ ├── C_06_DateAndTimeAPI_Exercises.java │ ├── C_04_Streams_Exercises.java │ ├── C_11_MissionControl.java │ ├── C_00_ConceptsOfJava5.java │ ├── C_03_MethodReferences.java │ ├── C_08_Annotations.java │ ├── C_01_FunctionalInterfacesAndLambdas_Exercises.java │ ├── C_02_Default_Methods.java │ ├── C_13_Stream_Best_Practice.java │ ├── C_13_Fancy_Stream_Migration_Patterns.java │ ├── C_05_ClassifyingStreams.java │ ├── C_10_SomeOtherStuff.java │ ├── C_05_Streams_Exercises.java │ ├── C_07_Concurrency.java │ ├── C_01_FunctionalInterfacesAndLambdas.java │ ├── C_06_DateAndTimeAPI.java │ ├── C_04_Streams.java │ └── C_09_JavaFX.java ├── .travis.yml ├── shippable.yml ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /.project 3 | /.classpath 4 | /.idea 5 | /bin 6 | *.iml 7 | /target/ -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/FunctionalInterfaceGen2.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | @FunctionalInterface 4 | public interface FunctionalInterfaceGen2 { 5 | public void doSomeOtherStuff(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/ChangeLog.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | /** 4 | * Container-Annotation to make {@link Change} repeatable. 5 | */ 6 | @interface ChangeLog { 7 | Change[] value(); 8 | } 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # language 2 | language: java 3 | 4 | # version numbers 5 | jdk: 6 | - oraclejdk8 7 | 8 | # Use JDK 8u45. The above line just causes the use of a minor version of Java 8. 9 | addons: 10 | apt: 11 | packages: 12 | - oracle-java8-installer 13 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/DefaultMethodImplementingEmptyClass.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | public class DefaultMethodImplementingEmptyClass implements InterfaceWithDefaultMethod { 4 | 5 | // OH NO! Nothing in here! But it's ok :) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/SlightlyMoreSophisticatedFunctionalInterface.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | @FunctionalInterface 4 | public interface SlightlyMoreSophisticatedFunctionalInterface { 5 | 6 | public int sumItUp(int summand1, int summand2); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/DeepThought.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | public class DeepThought implements SimpleFunctionalInterface { 4 | 5 | @Override 6 | public int returnAnswerToUltimateQuestion() { 7 | return 42; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/DefaultMethodSame1.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | /** 4 | * Functional interface with a default method. 5 | */ 6 | public interface DefaultMethodSame1 { 7 | 8 | default void foo() { 9 | System.out.println("1"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/DefaultMethodSame2.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | /** 4 | * Functional interface with a default method. 5 | */ 6 | public interface DefaultMethodSame2 { 7 | 8 | default void foo() { 9 | System.out.println("2"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/InterfaceWithStaticMethod.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | public interface InterfaceWithStaticMethod { 4 | 5 | static public int staticMethodWithinAnInterface() { 6 | System.out.println("Who would've thought this ..."); 7 | return 1; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/Change.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import java.lang.annotation.Repeatable; 4 | 5 | /** 6 | * Annotation that tells when and why a change was made. 7 | */ 8 | @Repeatable(value = ChangeLog.class) 9 | @interface Change { 10 | String date(); 11 | String reason(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/DefaultMethodOverridingClass.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | public class DefaultMethodOverridingClass implements InterfaceWithDefaultMethod { 4 | 5 | // This is an overwrite for the default method in the interface: 6 | public int addStuff(int base) { 7 | return 12 + base; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/InterfaceWithDefaultMethodGen2.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | public interface InterfaceWithDefaultMethodGen2 extends InterfaceWithDefaultMethodGen1 { 4 | 5 | // This hectic method overrides the older method in the parent interface. 6 | default String getSomeString() { 7 | return "Hi! I'm the fancy dancy fresh gen 2!"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/InterfaceWithDefaultMethodGen1.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | public interface InterfaceWithDefaultMethodGen1 { 4 | 5 | // Although this likable and polite method doesn't harm anybody, it gets overridden by the younger generation and 6 | // is never called. Sad. 7 | default String getSomeString() { 8 | return "Hello there. I am the honorable generation 1."; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/InterfaceWithDefaultMethod.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | public interface InterfaceWithDefaultMethod { 4 | 5 | // Watch the new "default" key word! It's the new dude around the block, be nice! The dude brings a cool feature: 6 | // We can now have an implementation within an interface. Insane these youngsters these days ... 7 | default public int addStuff(int base) { 8 | return 42 + base; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/FunctionalInterfaceGen1.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | @FunctionalInterface 4 | public interface FunctionalInterfaceGen1 { 5 | // This is not allowed because the resulting interface would have two methods and hence cannot be a functional 6 | // interface. 7 | //public interface de.stevenschwenke.java.java8workshop.FunctionalInterfaceGen1 extends de.stevenschwenke.java.java8workshop.FunctionalInterfaceGen2 { 8 | public void doStuff(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/HighlySophisticatedFunctionalInterface.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | @FunctionalInterface 4 | public interface HighlySophisticatedFunctionalInterface { 5 | 6 | // This is the one and only abstract method allowed in this functional interface. 7 | public int returnAnswerToUltimateQuestionOfLifeTheUniverseAndEverything(); 8 | 9 | // But if there's a default implementation, it's alright: 10 | default public String returnQuestionTo42() { 11 | return "UH-OH"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/SimpleFunctionalInterface.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | // This is just a marker annotation! Can be removed but nice to have here for beginners. ;) 4 | @FunctionalInterface 5 | public interface SimpleFunctionalInterface { 6 | 7 | // This is the one and only abstract method allowed in this functional interface. 8 | public int returnAnswerToUltimateQuestion(); 9 | 10 | // That one is not allowed: 11 | // public int returnAnotherAnswerToUltimateQuestionOfLifeTheUniverseAndEverything(); 12 | } 13 | -------------------------------------------------------------------------------- /shippable.yml: -------------------------------------------------------------------------------- 1 | # language 2 | language: java 3 | 4 | # version numbers 5 | jdk: 6 | - oraclejdk8 7 | 8 | before_install: 9 | - apt-get update 10 | - apt-get install -y maven 11 | 12 | before_script: 13 | - if [[ $SHIPPABLE_JDK_VERSION == "oraclejdk8" ]] ; then export JAVA_HOME="/usr/lib/jvm/java-8-oracle"; export PATH="$PATH:/usr/lib/jvm/java-8-oracle/bin"; export java_path="/usr/lib/jvm/java-8-oracle/jre/bin/java"; fi 14 | - update-alternatives --set java $java_path 15 | - java -version 16 | 17 | after_success: 18 | - mvn clean cobertura:cobertura 19 | - mvn test -------------------------------------------------------------------------------- /src/main/java/de/stevenschwenke/java/java8workshop/JavaVersion.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Target; 5 | 6 | /** 7 | * Annotation that tells the Java version. 8 | */ 9 | @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType 10 | .LOCAL_VARIABLE, ElementType.ANNOTATION_TYPE, ElementType.PACKAGE, ElementType.TYPE_PARAMETER, ElementType 11 | .TYPE_USE}) // The last two are since 1.8! 12 | @interface JavaVersion { 13 | 14 | String value(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_03_MethodReferences_Exercises.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | /** 4 | * Exercises for chapter 3: method references. 5 | */ 6 | public class C_03_MethodReferences_Exercises { 7 | 8 | /* 9 | Write code that prints a simple address list (just Strings) to the console. Use method references! 10 | */ 11 | 12 | 13 | // Repetition is a source of learning: 14 | // What are default methods? 15 | // Why have they been introduced? 16 | // Given an interface with a default method and an implementing class that has a method with the same signature 17 | // as the default method. Which implementation will be called? 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_12_Outlook.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | /** 4 | * Some cool stuff is planned for the next Java version. 5 | */ 6 | public class C_12_Outlook { 7 | 8 | /* 9 | Project Jigsaw: 10 | - openjdk.java.net/projects/jigsaw 11 | - "Make the Java SE Platform, and the JDK, more easily scalable down to small computing devices" 12 | - "Improve the security and maintainability of Java SE Platform Implementations in general, and the JDK in 13 | particular" 14 | - "Enable improved application performance" 15 | - "Make it easier for developers to construct and maintain libraries and large applications, for both the 16 | Java SE and EE Platforms" 17 | 18 | Project Sumatra 19 | - openjdk.java.net/projects/sumatra 20 | - improve performance through usage of GPUs and accelerated processing units (APUs) 21 | 22 | 23 | */ 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_06_DateAndTimeAPI_Exercises.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | /** 4 | * Exercises for chapter 6: Date and Time API. 5 | */ 6 | public class C_06_DateAndTimeAPI_Exercises { 7 | 8 | /* 9 | Task 1: 10 | Write code that prints out your birthday with the day of the week for the next 20 years. 11 | 12 | Task 2: 13 | Expand your code from above and proof that there is exactly one year between your birthdays. 14 | 15 | Task 3: 16 | Expand your code from above and print out the day of week of your birthday in Japan. 17 | 18 | Task 4: 19 | Expand your code from above with stop watch code and print out the total time needed to run your code. 20 | */ 21 | 22 | // Repetition is a source of learning: 23 | // What is a stream? 24 | // If the backing list of a stream is altered, what will happen to the stream? 25 | // Which kinds of operations are there? 26 | // Given a parallel stream that is executed a number of times. What can we say with certainty about the order 27 | // of execution of the operations? 28 | // When do intermediate operations of streams get executed? 29 | // Is every single intermediate operation executed in every stream? 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_04_Streams_Exercises.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | /** 4 | * Exercises for chapter 4: Streams. 5 | */ 6 | public class C_04_Streams_Exercises { 7 | 8 | /* 9 | Task 1: 10 | a) Create a stream with the names of the attendants of this course. Traverse the stream and print out 11 | the names. 12 | b) Create a parallel stream and do the same. Is the output different? Why? 13 | 14 | Task 2: 15 | Expand your code from task 1 and filter out every name containing the letter "e". How many elements does 16 | the stream have now? 17 | 18 | Task 3: 19 | See task 2 from chapter 1. Use streams to simplify the Test. 20 | */ 21 | 22 | 23 | 24 | 25 | 26 | 27 | // Intentionally left blank 28 | 29 | 30 | 31 | 32 | 33 | /* 34 | Solution task 3: Remove evaluate-method and use the following code: 35 | 36 | Stream goodCarsStream = drivables.stream().filter(predicate); 37 | goodCarsStream.forEach(o -> System.out.println(o.drive())); 38 | */ 39 | 40 | 41 | // Repetition is a source of learning: 42 | // What are method references? 43 | // When are method references called? 44 | // Can there be a method reference for a constructor? 45 | } 46 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | de.stevenschwenke.java 8 | Java8Workshop 9 | 1.1 10 | 11 | 12 | UTF-8 13 | 1.8 14 | 1.8 15 | 16 | 17 | 18 | 19 | junit 20 | junit 21 | 4.12 22 | test 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.apache.maven.plugins 31 | maven-surefire-plugin 32 | 2.20.1 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_11_MissionControl.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | /** 4 | * MissionControl is the new tool for monitoring Java applications. 5 | */ 6 | public class C_11_MissionControl { 7 | /* 8 | See http://www.oracle.com/technetwork/java/javaseproducts/mission-control/java-mission-control-1998576.html 9 | and https://www.youtube.com/watch?v=ORHVOmxnxbo 10 | and especially http://blog.takipi.com/oracle-java-mission-control-the-ultimate-guide/ 11 | 12 | MissionControl "JMC" = JMX Console + FlightRecorder "JFR" 13 | 14 | JMX CONSOLE 15 | ============ 16 | 17 | 1. Start MissionControl: [JDK]/bin/jmc.exe 18 | 19 | 2. Create launch config with JVM startup flags for flight recorder: 20 | -XX:+UnlockCommercialFeatures 21 | -XX:+FlightRecorder 22 | 23 | 3. Right-Click in JVM Browser -> Start JMX Console 24 | 25 | 4. Overview, 26 | Triggers, 27 | Memory -> Heap Histogram (Click on "Refresh Heap Histogram" two times) 28 | Threads -> Deadlock-Detection + "Lock Owner Name" 29 | 30 | FLIGHT RECORDING 31 | ================= 32 | 33 | 1. (within MissionControl) Right-Click in JVM-Browser -> Start Flight Recording 34 | 2. Range Slider 35 | 3. Code -> Hot Methods 36 | 37 | HEAP DUMP ANALYSIS 38 | =================== 39 | 40 | 1. Install experimental plugin JOverflow via Help -> Install New Software -> Heap Analysis -> JOverflow Heap Analyzer 41 | 2. Right Click on Application -> Dump Heap 42 | */ 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_00_ConceptsOfJava5.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | /** 10 | * Let's start with some of the most basic concepts that got introduced with Java 1.5: Generics, for-each-loop and 11 | * varargs. 12 | */ 13 | public class C_00_ConceptsOfJava5 { 14 | 15 | @Test 16 | public void generics() { 17 | 18 | // Java 1.0 19 | List list = new ArrayList(Arrays.asList(1, 2, 3)); 20 | Object o = list.get(0); 21 | Integer i = (Integer) list.get(0); 22 | String s = (String) list.get(0); // exception at runtime! 23 | 24 | // Java 1.5 25 | List list5 = new ArrayList(Arrays.asList(1, 2, 3)); 26 | o = list5.get(0); 27 | i = list5.get(0); // no cast necessary! 28 | // s = (String) list5.get(0); // exception at compile-time! 29 | 30 | // Java 1.7 (Diamond operator) 31 | List list7 = new ArrayList<>(Arrays.asList(1, 2, 3)); 32 | } 33 | 34 | class MyGenericClass { 35 | private T myT; 36 | private X myX; 37 | private N number; 38 | } 39 | 40 | @Test 41 | public void usingOwnGenerics() { 42 | new MyGenericClass(); 43 | } 44 | 45 | @Test 46 | public void loopsInJava1_2And1_5(){ 47 | 48 | // Java 1.2 49 | List list = new ArrayList(Arrays.asList(1, 2, 3)); 50 | 51 | 52 | for (int i = 0; i < list.size(); i++) { 53 | System.out.println(list.get(i)); 54 | } 55 | 56 | // Java 5 57 | List list5 = new ArrayList<>(Arrays.asList(1, 2, 3)); 58 | 59 | for (Integer i : list5) { 60 | System.out.println(i); 61 | } 62 | } 63 | 64 | @Test 65 | public void varArgs() { 66 | varArgsMethod(1,2,3,4,5,6,7); 67 | } 68 | 69 | public void varArgsMethod(int x, int y, int... z) { 70 | for(Integer i : z) { 71 | System.out.println(i); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_03_MethodReferences.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.Comparator; 8 | import java.util.List; 9 | import java.util.function.BiFunction; 10 | import java.util.function.Consumer; 11 | import java.util.function.Function; 12 | import java.util.function.Supplier; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | 16 | /** 17 | * Method references allow to identify methods without actually calling them. 18 | */ 19 | public class C_03_MethodReferences { 20 | 21 | @Test 22 | public void methodReferencesForSortingAList() { 23 | 24 | List list = Arrays.asList("Tiny", "Much much long", "Normal", "Slightly long"); 25 | 26 | // Java 7: 27 | 28 | list.sort(new Comparator() { 29 | @Override 30 | public int compare(String o1, String o2) { 31 | if (o1.length() == o2.length()) return 0; 32 | return o1.length() > o2.length() ? 1 : -1; 33 | } 34 | }); 35 | System.out.println(String.join(", ", list)); 36 | 37 | // Java 8: 38 | list = Arrays.asList("Tiny", "Much much long", "Normal", "Slightly long"); 39 | 40 | list.sort(Comparator.comparingInt(String::length)); 41 | System.out.println(String.join(", ", list)); 42 | 43 | // Using method references for a print of the list (however without separators) 44 | list.forEach(System.out::print); 45 | } 46 | 47 | @Test 48 | public void methodReferenceToVariable() { 49 | // Consumer is a new functional interface that takes one argument and returns void. 50 | Consumer consumerReference = System.out::println; 51 | consumerReference.accept("Printed string by reference to variable!"); 52 | 53 | // Supplier is also a new functional interface: 54 | Supplier supplierReference = Math::random; 55 | consumerReference.accept(supplierReference.toString()); 56 | 57 | // Wait, we wanted to print that random number! With .get() we get the value of a supplier: 58 | consumerReference.accept(supplierReference.get().toString()); 59 | } 60 | 61 | @Test 62 | public void constructorMethodReferences() { 63 | Function x = Integer::new; 64 | Integer constructedInteger = x.apply(42); 65 | assertEquals(new Integer(42), constructedInteger); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_08_Annotations.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import de.stevenschwenke.java.java8workshop.Change; 4 | import de.stevenschwenke.java.java8workshop.JavaVersion; 5 | import org.junit.Test; 6 | 7 | import java.util.List; 8 | 9 | @JavaVersion("1.5") 10 | public class C_08_Annotations { 11 | 12 | /* 13 | See http://docs.oracle.com/javase/tutorial/java/annotations/basics.html: 14 | Annotations can be applied to declarations: declarations of classes, fields, methods, and other program 15 | elements. When used on a declaration, each annotation often appears, by convention, on its own line. 16 | 17 | As of the Java SE 8 release, annotations can also be applied to the use of types. Here are some examples: 18 | 1. Class instance creation expression: 19 | new @Interned MyObject(); 20 | 21 | 2. Type cast: 22 | myString = (@NonNull String) str; 23 | 24 | 3. implements clause: 25 | class UnmodifiableList implements 26 | @Readonly List<@Readonly T> { ... } 27 | 28 | 4. Thrown exception declaration: 29 | void monitorTemperature() throws 30 | @Critical TemperatureException { ... } 31 | */ 32 | 33 | private @JavaVersion("1.5") String attributeString; 34 | 35 | @JavaVersion("1.5") 36 | private String otherAttributeString; 37 | 38 | @JavaVersion("1.5") 39 | private void doNothing() { 40 | 41 | } 42 | 43 | @Test 44 | public void annotationExamples() throws @JavaVersion("1.8") Exception { 45 | 46 | @JavaVersion("1.5") 47 | String someString0 = null; 48 | 49 | String someString1 = new @JavaVersion("1.8") String("someString"); 50 | 51 | String someString2 = (@JavaVersion("1.8") String) someString1; 52 | 53 | List<@JavaVersion("1.8") String> strings; 54 | 55 | /* 56 | That looks cool, but why?! Imagine annotations that tell something about the USE of a type, not just the 57 | DECLARATION of a type. Imagine a method annotated with @NonNullArguments can get parsed if it really 58 | doesn't get null. 59 | */ 60 | } 61 | 62 | @Change(date = "2015-03-15", reason="First version of this method") 63 | @Change(date = "2015-03-18", reason="small bug fix") 64 | public void repeatableAnnotations() { 65 | 66 | } 67 | 68 | // Repetition is a source of learning: 69 | // Remember: Callable is designed to be used with executor, not stand-alone. 70 | // Since Java 8, fluent API possible even with concurrent code. 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_01_FunctionalInterfacesAndLambdas_Exercises.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.function.Predicate; 9 | 10 | /** 11 | * Exercises for chapter 1: functional interfaces and lambdas. 12 | */ 13 | public class C_01_FunctionalInterfacesAndLambdas_Exercises { 14 | 15 | /* 16 | Task 1: 17 | Write the following code using both functional interfaces and lambdas: 18 | 1. Interface "Drivable" for a thing that can drive. Driving things return a String when being asked to drive. 19 | 2. Class "GermanAutobahn" that holds any number of Drivables. There has to be a method printTrafficReport() 20 | that prints a messages what's currently going on on the autobahn. 21 | 3. Test-method that creates a GermanAutobahn, sets a number of Drivables in it and prints a traffic report to 22 | console. 23 | */ 24 | 25 | 26 | 27 | 28 | 29 | // Intentionally left blank 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | public interface Drivable { 39 | String drive(); 40 | } 41 | 42 | public class GermanAutobahn { 43 | 44 | private List driveables = new ArrayList<>(); 45 | 46 | public void addDrivable(Drivable d) { 47 | driveables.add(d); 48 | } 49 | 50 | public void printTrafficReport() { 51 | System.out.println("On the autobahn there are:"); 52 | driveables.forEach(x -> System.out.println(x.drive())); 53 | } 54 | } 55 | 56 | @Test 57 | public void test() { 58 | GermanAutobahn germanAutobahn = new GermanAutobahn(); 59 | germanAutobahn.addDrivable(() -> "Driving blue car"); 60 | germanAutobahn.addDrivable(() -> "Driving red car"); 61 | germanAutobahn.printTrafficReport(); 62 | } 63 | 64 | /* 65 | Task 2: 66 | The autobahn shall not be used by blue cars. Blue cars are bad because ... they are blue! Write a Predicate 67 | that filters out blue cars from the autobahn! 68 | */ 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | // Intentionally left blank 78 | 79 | 80 | 81 | @Test 82 | public void filterOutBlueCars() { 83 | 84 | List drivables = Arrays.asList(() -> "blue car", () -> "red car", () -> "orange car"); 85 | 86 | Predicate predicate = o -> !o.drive().contains("blue"); 87 | 88 | evaluate(drivables, predicate); 89 | } 90 | 91 | private void evaluate(List list, Predicate predicate) { 92 | for (Drivable i : list) { 93 | if (predicate.test(i)) { 94 | System.out.println(i.drive()); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_02_Default_Methods.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import de.stevenschwenke.java.java8workshop.*; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertNotNull; 8 | 9 | /** 10 | * Default methods have been introduced to make the old Java API compatible with the new concepts. 11 | */ 12 | public class C_02_Default_Methods { 13 | 14 | @Test 15 | public void interfaceWithDefaultMethod() { 16 | InterfaceWithDefaultMethod i = new DefaultMethodImplementingEmptyClass(); 17 | 18 | // Default: 19 | assertEquals(45, i.addStuff(3)); 20 | 21 | // Overwrite: 22 | InterfaceWithDefaultMethod inter2 = new DefaultMethodOverridingClass(); 23 | assertEquals(15, inter2.addStuff(3)); 24 | } 25 | 26 | @Test 27 | public void functionalInterfaceAndDefaultMethodTogether() { 28 | 29 | HighlySophisticatedFunctionalInterface inter = () -> 42; 30 | 31 | assertEquals(42, inter.returnAnswerToUltimateQuestionOfLifeTheUniverseAndEverything()); 32 | assertEquals("UH-OH", inter.returnQuestionTo42()); 33 | } 34 | 35 | /** 36 | * Default methods will be overridden by lower implementations. 37 | */ 38 | @Test 39 | public void overridingDefaultMethods() { 40 | InterfaceWithDefaultMethodGen2 gen2 = new InterfaceWithDefaultMethodGen2() { 41 | }; 42 | System.out.println(gen2.getSomeString()); 43 | } 44 | 45 | /** 46 | * This class inherits unrelated defaults for the method foo() and thus cannot exist: 47 | */ 48 | // class NameConflicts implements de.stevenschwenke.java.java8workshop.DefaultMethodSame1, de.stevenschwenke.java.java8workshop.DefaultMethodSame2 { 49 | // } 50 | 51 | 52 | 53 | // Let's take a break here. 54 | // 55 | // Default methods seem to be a really nice add-on to the language. However, they have been introduced mainly 56 | // to make the new Stream-API possible. The interface Iterable got the new method forEach(). This new method has 57 | // a default implementation. Otherwise, all classes implementing Iterable would have had to be rewritten with an 58 | // implementation of this new method. 59 | // The common developer may use default methods, but there are several blog posts that suggest not to use them 60 | // as a regular tool. Doing so could result in a mess if your inheritance hierarchy is more complex. Imagine 61 | // having to "climb up" the inheritance tree to find out what interface overrides which default method. That's 62 | // just one additional layer of complexity. To avoid problems here, continue implementing interfaces. Use default 63 | // methods only for what they have been introduced: Enhancing old APIs that have to be downwards compatible. 64 | 65 | // The above is the reason there are no exercises in this chapter. 66 | 67 | // Repetition is a source of learning: 68 | // What is a functional interface? 69 | // Is the annotation @FunctionalInterface necessary? 70 | // What is a lambda? 71 | // What means "effectively final"? 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_13_Stream_Best_Practice.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Optional; 9 | 10 | import static org.junit.Assert.assertNotNull; 11 | 12 | /** 13 | * Those examples are from a project I'm working in. 14 | * 15 | * @author dmitrij-drandarov 16 | * @since 14 Feb 17 17 | */ 18 | public class C_13_Stream_Best_Practice { 19 | 20 | private Map dummyFruits; 21 | 22 | @Before 23 | void dummy() { 24 | dummyFruits = new HashMap<>(); 25 | dummyFruits.put(1, new DummyFruit("Baby Banana", "It's yellow!", 20.0, DummyFruit.TYPE.BANANA)); 26 | dummyFruits.put(2, new DummyFruit("Granny Smith Apple", "Delicious!", 10.5, DummyFruit.TYPE.APPLE)); 27 | dummyFruits.put(3, new DummyFruit("Grapefruit", "It's totally an orange!", 8.5, DummyFruit.TYPE.ORANGE)); 28 | } 29 | 30 | /** 31 | * A common mistake that people make when using Optionals, is using the {@link Optional#get}-Method without using 32 | * {@link Optional#isPresent()} to check for its, well... presence. 33 | * 34 | * So instead you should use {@link Optional#orElse} or any of it's equivalents. This way you can define exactly 35 | * what you want if that {@link Optional} is indeed empty. It can be either a 'null', an empty variance of that Pojo 36 | * you're searching or whatever else you need for a certain use case. However in any case you will exactly know what 37 | * you'll be getting. 38 | * Also --> Readabilty! 39 | */ 40 | @Test 41 | void mistakesWithOptionals() { 42 | //Meh. 43 | DummyFruit foundFruit1 = dummyFruits.values().stream() 44 | .filter(dummyFruit -> "Baby Banana".equals(dummyFruit.getName())).findFirst().get(); 45 | assertNotNull(foundFruit1); //^ unchecked 46 | 47 | //Better 48 | DummyFruit foundFruit2 = dummyFruits.values().stream() 49 | .filter(dummyFruit -> "Baby Banana".equals(dummyFruit.getName())).findFirst().orElse(null); 50 | assertNotNull(foundFruit2); //^ save and readable 51 | } 52 | 53 | 54 | 55 | /** Simple Pojo-Class */ 56 | private static class DummyFruit { 57 | 58 | private String name; 59 | private String description; 60 | private double value; 61 | private TYPE type; 62 | 63 | enum TYPE { ORANGE, APPLE, BANANA } 64 | 65 | DummyFruit(String name, String description, double value, TYPE type) { 66 | this.name = name; 67 | this.description = description; 68 | this.value = value; 69 | this.type = type; 70 | } 71 | 72 | String getName() { 73 | return name; 74 | } 75 | String getDescription() { 76 | return name; 77 | } 78 | double getValue() { 79 | return value; 80 | } 81 | TYPE getType() { 82 | return type; 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Java 8 Workshop 2 | ============== 3 | 4 | Shippable status: 5 | [![Build Status](https://api.shippable.com/projects/5593645bedd7f2c0524cb0ba/badge/master)](https://app.shippable.com/projects/5593645bedd7f2c0524cb0ba/builds/latest) 6 | 7 | Travis status: 8 | [![Build Status](https://travis-ci.org/stevenschwenke/Java8Workshop.svg?branch=master)](https://travis-ci.org/stevenschwenke/Java8Workshop) 9 | 10 | Meta: About the workshop 11 | -------------------------- 12 | The workshop is an internal event for my coworkers. Target audience are experienced Java developers who are 13 | interested in the new features of Java 8 (all updates included). Goals of the workshop are 14 | 15 | - having a fair overview over the new Java 8 features 16 | - having a set of code examples to use as templates 17 | - having a better knowledge about what Java version brings which features - however focus of this course is Java 8! 18 | 19 | How to use this 20 | ----------------- 21 | This repository is supposed to be a code base for own experiments and a reference to go to while writing own code. 22 | Hence, it is not necessary to vocally explain every line of code to the participants. 23 | 24 | Content 25 | --------- 26 | 0. concepts of Java 1.5: Generics, for-each-loop and varargs 27 | 1. functional interfaces and lambdas + exercises 28 | 2. default methods 29 | 3. method references + exercises 30 | 4. streams + exercises 31 | 5. Classifying streams 32 | 6. Date and Time API + exercises 33 | 7. Concurrency 34 | 8. Annotations 35 | 9. JavaFX 36 | 10. Other stuff 37 | 11. MissionControl 38 | 12. Outlook 39 | 40 | What this course does NOT cover: 41 | 42 | - Deep insight into concurrency. In fact, this topic is only scratched on the surface. 43 | - Foundation of JavaFX. Go to https://github.com/stevenschwenke/JavaFXWorkshop for that one. 44 | 45 | 46 | The workshop is based on unit tests that have a lot of comments to explain what's going on. This way, 47 | everyone can go through the workshop alone and without help. Each unit test class covers one aspect of the new 48 | features of Java 8. Feel free to pick one and read through the file. I recommend to read the tests from top to 49 | bottom because they depend on each other. Yeah I know, that's not what unit tests are for. But I don't care. ;) 50 | 51 | **Attention: There are hideous unit tests that do weird things such as not terminating. Please don't run all unit 52 | tests at once and expect them to be green.** 53 | 54 | Feel free to give feedback to steven@stevenschwenke.de 55 | 56 | Meta: Copyright 57 | ---------------- 58 | All files in this repository are under Creative Commons 4.0 (see http://creativecommons.org/licenses/by/4.0/). 59 | 60 | You are free to: 61 | 62 | - Share — copy and redistribute the material in any medium or format 63 | - Adapt — remix, transform, and build upon the material for any purpose, even commercially. 64 | 65 | The licensor cannot revoke these freedoms as long as you follow the license terms. 66 | 67 | Under the following terms: 68 | 69 | - Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 70 | - No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_13_Fancy_Stream_Migration_Patterns.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import static org.junit.Assert.assertNotNull; 10 | import static org.junit.Assert.assertTrue; 11 | 12 | /** 13 | * Those examples are from a project I'm working in. I recently refactored them to Streams and wanted to share those 14 | * results. These examples are also not automatically convertible to Streams by IDEs right now. 15 | * 16 | * Rule of thumb: If you don't know whether you can implement a certain use case with streams, you probably can, however 17 | * just put a stream in your favorite IDE (preferably IntelliJ IDEA ;p) and use auto-completion. If there is a method 18 | * that sounds like your use case, it will probably work. 19 | * 20 | * @author dmitrij-drandarov 21 | * @since 14 Feb 17 22 | */ 23 | public class C_13_Fancy_Stream_Migration_Patterns { 24 | 25 | private Map dummyFruits; 26 | 27 | @Before 28 | public void dummy() { 29 | dummyFruits = new HashMap<>(); 30 | dummyFruits.put(1, new DummyFruit("Baby Banana", "It's yellow!", 20.0, DummyFruit.TYPE.BANANA)); 31 | dummyFruits.put(2, new DummyFruit("Granny Smith Apple", "Delicious!", 10.5, DummyFruit.TYPE.APPLE)); 32 | dummyFruits.put(3, new DummyFruit("Grapefruit", "It's totally an orange!", 8.5, DummyFruit.TYPE.ORANGE)); 33 | } 34 | 35 | /** 36 | * A simple old pattern of iteration over your {@link java.util.Collection} and finding the first (and presumably 37 | * exact) match to your query. 38 | */ 39 | @Test 40 | public void findFirstPattern() { 41 | DummyFruit dummyFruit1 = null; 42 | for (final DummyFruit DummyFruit : dummyFruits.values()) { 43 | if (DummyFruit.getName().equals("Grapefruit")) { 44 | dummyFruit1 = DummyFruit; 45 | break; 46 | } 47 | } 48 | assertNotNull(dummyFruit1); 49 | 50 | /* 51 | | | | | | | | 52 | v v v v v v v 53 | */ 54 | 55 | //Better and sexier code 56 | assertNotNull(dummyFruits.values().stream() 57 | .filter(dummyFruit -> "Grapefruit".equals(dummyFruit.getName())).findFirst().orElse(null)); 58 | } 59 | 60 | /** 61 | * Just as simple as the one above, this is basically a contains check that incorporates some sort of field of the 62 | * pojo so a simple {@link java.util.List#contains(Object)} won't do it. There are also other kinds of data models 63 | * that don't include any kind of containment-check. 64 | */ 65 | @Test 66 | public void anyMatchPattern() { 67 | boolean fruitFound = false; 68 | for(DummyFruit DummyFruit : dummyFruits.values()) { 69 | if("Baby Banana".equals(DummyFruit.getName())) { 70 | fruitFound = true; 71 | } 72 | } 73 | assertTrue(fruitFound); 74 | 75 | /* 76 | | | | | | | | 77 | v v v v v v v 78 | */ 79 | 80 | //Better and sexier code 81 | assertTrue(dummyFruits.values().stream().anyMatch(DummyFruit -> "Baby Banana".equals(DummyFruit.getName()))); 82 | } 83 | 84 | 85 | 86 | /** Simple Pojo-Class */ 87 | private static class DummyFruit { 88 | 89 | private String name; 90 | private String description; 91 | private double value; 92 | private TYPE type; 93 | 94 | enum TYPE { ORANGE, APPLE, BANANA } 95 | 96 | DummyFruit(String name, String description, double value, TYPE type) { 97 | this.name = name; 98 | this.description = description; 99 | this.value = value; 100 | this.type = type; 101 | } 102 | 103 | String getName() { 104 | return name; 105 | } 106 | String getDescription() { 107 | return name; 108 | } 109 | double getValue() { 110 | return value; 111 | } 112 | TYPE getType() { 113 | return type; 114 | } 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_05_ClassifyingStreams.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.Arrays; 6 | import java.util.stream.Stream; 7 | 8 | import static org.junit.Assert.assertTrue; 9 | 10 | /** 11 | * In Java Doc special wording for classifying streams-operations. For example operation limit(...) "This is a 12 | * short-circuiting stateful intermediate operation". This class explains these. 13 | */ 14 | public class C_05_ClassifyingStreams { 15 | 16 | /* 17 | intermediate vs terminal operations: 18 | - intermediate operation returns stream, for example filter(...), map(...), peek(...) 19 | - terminal operations return something else and are the end of a list of operations, for example count(), reduce(...) 20 | */ 21 | @Test 22 | public void intermediateAndTerminalOperation() { 23 | 24 | System.out.println("No terminal operation:"); 25 | // No terminal operation, so the intermediate operation is not executed. 26 | Stream.of(1, 2, 3).peek(System.out::println); 27 | 28 | System.out.println("With terminal operation:"); 29 | // Ah yes - now we see intermediate operation stuff happening - thanks to the terminal operation! :) 30 | Stream integerStream = Stream.of(1, 2, 3); 31 | integerStream.peek(System.out::println).count(); 32 | integerStream.close(); 33 | 34 | // Little hint: You now know the intermediate operation peek(). It let's you output your stream without 35 | // ending it, not like forEach(System.out::println). Very handy for having a peek within your stream. 36 | } 37 | 38 | @Test 39 | public void streamsCanNotBeOperatedUponAfterTerminalOperation() { 40 | Stream stream = Stream.of(1, 2, 3); 41 | 42 | // operation possible because stream has not been operated upon: 43 | stream.peek(System.out::println).reduce(Integer::sum).get(); 44 | 45 | // operation NOT possible because stream has been operated upon: 46 | stream.reduce(Integer::sum).get(); 47 | } 48 | 49 | /* 50 | Workflow: Each intermediate operation generates a new stream that "knows" which operations to execute on which 51 | elements of the input-stream. However, these operations are executed when the terminal operation occurs, not 52 | before! Then, the elements of the backing data source are visited one after the other and each intermediate and the 53 | terminal operation is performed on each element. The operations are executed vertically, not horizontally. 54 | Let's say we have three intermediate operations and a stream of 5 elements. Then each of the 5 elements is visited 55 | after each other and with each visit, every one of the three operations is performed at the current element. This 56 | way, every element has to be visited only once. This workflow is also necessary for parallel streams. 57 | */ 58 | @Test 59 | public void showingWorkflow() { 60 | String[] txt = {"This", "is", "my", "little", "example", "text"}; 61 | int sum = Arrays.stream(txt). 62 | filter(s -> s.length() > 1). 63 | peek(System.out::println). 64 | map(String::length). 65 | peek(System.out::println). 66 | reduce(0, Integer::sum); 67 | System.out.println("Sum: " + sum); 68 | } 69 | 70 | /* 71 | Short-circuiting operations 72 | 73 | = operation that doesn't visit all elements of a stream. Can be an intermediate of a final operation. 74 | */ 75 | @Test 76 | public void shortCircuitingOperation() { 77 | // Intermediate short-circuit operation that doesn't visit the last element: 78 | Stream.of(1, 2, 3).limit(2).forEach(System.out::println); 79 | 80 | // Terminal short-circuit operation that skips every element after the first "1": 81 | boolean integerOnePresent = Stream.of(1, 2, 3).anyMatch(integer -> integer == 1); 82 | assertTrue(integerOnePresent); 83 | } 84 | 85 | /* 86 | Stateless vs stateful operations 87 | 88 | Stateless operations just need the element to operate and don't need to know anything about the rest of the stream. 89 | Examples are filter, map. 90 | 91 | Stateful operations need information about the rest of the stream. limit(...) for example needs to count the visited 92 | elements in order to know when to cancel and return. distinct(...) removes duplicates and hence has to remember the 93 | visited elements. 94 | 95 | Stateless operations are fast and don't cause trouble when being called in parallel streams. Stateful operations 96 | however have to be treated differently and are much slower. The most expensive operation is sort(...) because 97 | it has to copy the whole stream in order to compare its elements. 98 | */ 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_10_SomeOtherStuff.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import de.stevenschwenke.java.java8workshop.InterfaceWithStaticMethod; 4 | import org.junit.Test; 5 | 6 | import javax.script.ScriptEngine; 7 | import javax.script.ScriptEngineManager; 8 | import javax.script.ScriptException; 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | import java.util.Optional; 13 | import java.util.stream.Stream; 14 | 15 | import static junit.framework.Assert.fail; 16 | import static org.junit.Assert.assertEquals; 17 | 18 | /** 19 | * This class is for small features that didn't fit in the other test classes. 20 | */ 21 | public class C_10_SomeOtherStuff { 22 | 23 | /** 24 | * Interfaces are allowed to have static methods now. 25 | */ 26 | @Test 27 | public void interfacesStaticMethods() { 28 | assertEquals(1, InterfaceWithStaticMethod.staticMethodWithinAnInterface()); 29 | } 30 | 31 | /* 32 | Permanent generation is gone. All data that lived in this fixed-sized memory will be in the new Metaspace 33 | that can expand at runtime. This however bloats the JVM process until the machine dies on low memory. Can be 34 | avoided with -XX:MaxMetaspaceSize. 35 | More at http://stackoverflow.com/questions/20563047/jdk-8-memory-layout-and-garbage-collection 36 | */ 37 | 38 | /** 39 | * Ability of the compiler to recognize the type from context improved. 40 | */ 41 | @Test 42 | public void improvedTypeDeduction() { 43 | 44 | // Worked since there are generics: 45 | List list = new ArrayList<>(); 46 | 47 | // Works since Java 8: 48 | list = Collections.synchronizedList(new ArrayList<>()); 49 | } 50 | 51 | /** 52 | *

53 | * Since forever: JavaScript-engine Rhino. Gets replaced with Nashorn in Java 8. 54 | *

55 | *

    56 | *
  • Nashorn 2 to 10 times faster than Rhino.
  • 57 | *
  • Enables "free standing JavaScript applications using the jrunscript command-line tool" 58 | * (see openjdk.java.net/projects/nashorn).
  • 59 | *
  • Nashorn: 100% ECMA-Script conform, Rhino just 95.9%
  • 60 | *
61 | *

62 | * Reasons for Java supporting JavaScript: 63 | *
    64 | *
  • enabling user to script dynamically
  • 65 | *
  • establishing JVM as platform for more programming languages
  • 66 | *
  • script languages have less boiler plate code and are often easier to read
  • 67 | *
  • frontend developer (if there is such a thing these days) can code backend without learning a new language
  • 68 | *
69 | */ 70 | @Test 71 | public void javaScript() throws ScriptException { 72 | // CLI: run bin/jjs: 73 | 74 | // jjs> print("This is JavaScript!"); 75 | // jjs> quit(); 76 | 77 | // CLI with js file: 78 | // bin/jjs d:\myJavaScript.js 79 | 80 | // in Java: 81 | ScriptEngineManager factory = new ScriptEngineManager(); 82 | ScriptEngine nashorn = factory.getEngineByName("nashorn"); 83 | nashorn.eval("print(\"This is JavaScript in Java\");"); 84 | 85 | // There are more features, like compiling code to bytecode. Not looked into here. :) 86 | } 87 | 88 | @Test 89 | public void optional() { 90 | 91 | /* 92 | java.util.Optional 93 | 94 | - can contain a value or be empty - in other words: has an optional value 95 | - answers the question if there is a result or not (and, if present, encapsulates the result) 96 | - return-type of some stream operations 97 | */ 98 | 99 | // This is easy: 100 | Stream integerStream = Stream.of(-2, -1, 0, 1, 2); 101 | int sum = integerStream.reduce(0, (a, b) -> a + b); 102 | assertEquals(0, sum); 103 | 104 | // The sum of this empty stream is 0, as above. However, this 0 results from the identity 105 | // given to the reduce() - method. So in this case, the result is of complete different nature. 106 | Stream emptyStream = Stream.of(); 107 | sum = emptyStream.reduce(0, (a, b) -> a + b); 108 | assertEquals(0, sum); 109 | 110 | // Here comes the Optional: 111 | emptyStream = Stream.of(-2,-1,0,1,2); 112 | Optional optional = emptyStream.reduce((a, b) -> a + b); 113 | assertEquals(0, optional.get().intValue()); // OK 114 | 115 | emptyStream = Stream.of(); 116 | optional = emptyStream.reduce((a, b) -> a + b); 117 | // assertEquals(0, optional.get().intValue()); // "NoSuchElementException: No value present" 118 | 119 | // Better: 120 | if(optional.isPresent()) { 121 | assertEquals(0, optional.get().intValue()); 122 | } else { 123 | fail("Hey, your stream was empty!"); 124 | } 125 | 126 | // -> Optional as a NullObject (http://en.wikipedia.org/wiki/Null_Object_pattern) 127 | 128 | // Creating Optional objects: 129 | Optional.empty(); 130 | Optional.of("String"); 131 | Optional.ofNullable(null); 132 | 133 | // And now all together with some eye candy: 134 | Stream.of(-2,-1,0,1,2).reduce((a,b)->a+b).ifPresent(s -> System.out.println(s)); 135 | } 136 | 137 | 138 | // Repetition is a source of learning: 139 | // There's a new class in Java 8 which allows filtering in lists. What's the name of this class? 140 | // There's a new method in the Task-class that gives information about the progress of a task. What's its name? 141 | // What does the new ScheduledService class do? 142 | } 143 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_05_Streams_Exercises.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.*; 6 | import java.util.concurrent.ForkJoinPool; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | import java.util.function.BinaryOperator; 9 | import java.util.function.Consumer; 10 | import java.util.function.Function; 11 | import java.util.function.Predicate; 12 | import java.util.stream.Stream; 13 | 14 | /** 15 | * Exercises for chapter 4 and 5: Streams. 16 | */ 17 | public class C_05_Streams_Exercises { 18 | 19 | // Exercise 1 - orderOfOperationsInStreams 20 | // The following code should print 3 random numbers that each are greater than 0.5. However, 21 | // it doesn't print 3 of these numbers every time. Sometimes there are only two or one. 22 | // Rewrite the code so that it prints 3 numbers. Shorten the code as much as possible. 23 | @Test 24 | public void orderOfOperationsInStreams() { 25 | 26 | System.out.println("Given:"); 27 | Stream.generate(Math::random).limit(3).filter(new Predicate() { 28 | @Override 29 | public boolean test(Double o) { 30 | return o > 0.5; 31 | } 32 | }).forEach((x) -> System.out.println(x)); 33 | 34 | // You can also write it like this: 35 | Stream.generate(Math::random).limit(3).filter(o -> o > 0.5).forEach(System.out::println); 36 | } 37 | 38 | // Exercise 2 - doingMathWithLambdas 39 | // Write conventional Java 7 code that calculates the formula y = sum(x*x + 5) for a list of integers. The result 40 | // should be printed as a single integer. When you are finished, rewrite the code to Java 8 using streams and 41 | // lambdas. 42 | 43 | // Exercise 3 - generatingIntegers 44 | // Write a generator that prints a number and its 100 following numbers. 45 | 46 | // Exercise 4 - generatingCalendarDays 47 | // Advanced task: write a generator that prints the current date and the date of the next 100 days. 48 | 49 | // Exercise 5 - reduceWithRobustErrorHandling 50 | // Write code that accepts a list/array of integers. Only the even numbers shall be printed out and summed. The sum 51 | // should also be printed. 52 | 53 | // Exercise 6 - showThatParallelStreamRunsOnDifferentThreads 54 | // Write code that shows how sequential streams run on one thread and parallel streams run on multiple threads. 55 | 56 | //////////////////////// 57 | // SOLUTIONS 58 | //////////////////////// 59 | 60 | @Test 61 | public void orderOfOperationsInStreamsSOLUTION() { 62 | 63 | // Solution: Streams execute operations in the given order. Here, the random numbers are limited to 3 and 64 | // then filtered. To guarantee that 3 numbers are printed, they should first be filtered and then limited: 65 | System.out.println("Solution:"); 66 | Stream.generate(Math::random).filter(o -> o > 0.5).limit(3).forEach((x) -> System.out.println(x)); 67 | } 68 | 69 | @Test 70 | public void doingMathWithLambdasSOLUTION() { 71 | 72 | List args = new ArrayList<>(Arrays.asList(new Integer[]{1, 2, 3, 4, 5})); 73 | 74 | System.out.println("Java 7:"); 75 | int resultJava7 = 0; 76 | for (Integer i : args) { 77 | resultJava7 += i * i + 5; 78 | } 79 | System.out.println(resultJava7); 80 | 81 | System.out.println("Java 8 uncool:"); 82 | Object resultJava8Uncool = args.stream().map(new Function() { 83 | @Override 84 | public Object apply(Integer integer) { 85 | return integer * integer + 5; 86 | } 87 | }).reduce(new BinaryOperator() { 88 | @Override 89 | public Object apply(Object o, Object o2) { 90 | return (Integer) o + (Integer) o2; 91 | } 92 | }).get(); 93 | System.out.println(resultJava8Uncool); 94 | 95 | System.out.println("Java 8:"); 96 | Object resultJava8 = args.stream().map(integer -> integer * integer + 5).reduce((o, o2) -> o + o2).get(); 97 | System.out.println(resultJava8); 98 | } 99 | 100 | @Test 101 | public void generatingIntegersSOLUTION() { 102 | Stream integerStream = Stream.generate(new AtomicInteger(5)::getAndIncrement).limit(100); 103 | integerStream.forEach(System.out::println); 104 | } 105 | 106 | @Test 107 | public void generatingCalendarDaysSOLUTION() { 108 | Stream dateStream = Stream.iterate(new Date(), date -> { 109 | Calendar c = new GregorianCalendar(); 110 | c.setTime(date); 111 | c.set(GregorianCalendar.DAY_OF_YEAR, c.get(GregorianCalendar.DAY_OF_YEAR) + 1); 112 | return c.getTime(); 113 | }); 114 | dateStream.limit(100).forEach(System.out::println); 115 | } 116 | 117 | @Test 118 | public void reduceWithRobustErrorHandlingSOLUTION() { 119 | Integer[] integers = {1, 2, 3, 4, 5}; 120 | 121 | Stream stream = Stream.of(integers).filter(integer -> integer % 2 == 0).peek(System.out::println); 122 | Optional reduce = stream.reduce(Integer::sum); 123 | 124 | // Ooops - this could throw a nasty exception if there are no values in the list any more. 125 | System.out.println(reduce.get()); 126 | 127 | // This is pretty robust and won't throw exceptions. 128 | System.out.println(reduce.orElse(0)); 129 | } 130 | 131 | @Test 132 | public void showThatParallelStreamRunsOnDifferentThreadsSOLUTION() { 133 | System.out.println("Sequential:"); 134 | Stream seqStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 135 | seqStream.forEach(i -> { 136 | System.out.println(Thread.currentThread().getName() + ": "+ i); 137 | }); 138 | 139 | System.out.println("Parallel:"); 140 | Stream parallelStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).parallel(); 141 | parallelStream.forEach(i -> { 142 | System.out.println(Thread.currentThread().getName() + ": "+ i); 143 | }); 144 | 145 | // ForkJoinPool uses the number of physical processors but can be configured to use an arbitrary number of 146 | // processors. However, using the physical processors is a good idea. 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_07_Concurrency.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Date; 7 | import java.util.List; 8 | import java.util.concurrent.*; 9 | import java.util.function.Supplier; 10 | 11 | /** 12 | * Java 8 brings a lot of support for concurrent programming. These are not main topic of this workshop and are just 13 | * mentioned here for further reading. This class will show you the evolution of concurrency classes in the JDKs on a 14 | * 10.000 ft level. 15 | */ 16 | public class C_07_Concurrency { 17 | 18 | /** 19 | * Since JDK 1.0: Runnable. 20 | *

21 | * Simple Runnable that cannot be parametrized, cannot throw a checked exception and returns nothing. It just runs. 22 | */ 23 | static class MyRunnable implements Runnable { 24 | @Override 25 | public void run() { 26 | System.out.println(Thread.currentThread().getName()); 27 | 28 | // Notice: no return value here! 29 | } 30 | } 31 | 32 | @Test 33 | public void runnableTest() { 34 | Thread t1 = new Thread(new MyRunnable()); 35 | Thread t2 = new Thread(new MyRunnable()); 36 | t1.start(); 37 | t2.start(); 38 | } 39 | 40 | /** 41 | * Since JDK 1.5: Callable. 42 | *

43 | * Here is a simple Callable that is parametrized with a Boolean. 44 | */ 45 | static class MyCallable implements Callable { 46 | 47 | // Notice: Can throw an exception! 48 | @Override 49 | public Boolean call() throws Exception { 50 | Thread.sleep(1000); // simulate some heavy work here 51 | System.out.println(Thread.currentThread().getName()); 52 | 53 | // Notice: Return value possible! 54 | return Boolean.TRUE; 55 | } 56 | } 57 | 58 | @Test 59 | public void callableTest() throws Exception { 60 | 61 | // As you see, Callable cannot instantiate a Thread directly. It abstracts the Thread to a higher level and 62 | // is intended to be used with ExecutorService. 63 | 64 | MyCallable callable1 = new MyCallable(); 65 | MyCallable callable2 = new MyCallable(); 66 | callable1.call(); 67 | callable2.call(); 68 | 69 | // As you can see, the Callables run on the same thread. To have more control and a comfortable API, there 70 | // are some useful classes. 71 | } 72 | 73 | /** 74 | * Callable is intended to be used in services like the Executor class. Let's have a look at this Callable: 75 | */ 76 | static class MyCallable2 implements Callable { 77 | 78 | private int id; 79 | 80 | public MyCallable2(int id) { 81 | this.id = id; 82 | } 83 | 84 | @Override 85 | public String call() throws Exception { 86 | Thread.sleep(1000); // simulate some heavy work here 87 | return Thread.currentThread().getName() + ": " + id; 88 | } 89 | } 90 | 91 | /** 92 | * ... In this test, it's wrapped in a Future and used with an Executor class. This makes it easy to work with 93 | * tasks. 94 | */ 95 | @Test 96 | public void callableWrappedInFuture() throws Exception { 97 | 98 | // The executor sets the environment for the Callable to run in, for example amount of threads. 99 | ExecutorService executor = Executors.newFixedThreadPool(10); 100 | 101 | List> list = new ArrayList<>(); 102 | 103 | // work with 100 instances of the callable 104 | for (int i = 0; i < 100; i++) { 105 | 106 | // The executor returns a Future. From JavaDoc: "The Future's get method will return the task's result 107 | // upon successful completion." 108 | Future future = executor.submit(new MyCallable2(i)); 109 | list.add(future); 110 | } 111 | 112 | // Future-objects represent the result of the callable. 113 | for (Future future : list) { 114 | // This line gets executed when the Future is ready. That causes the output delay in console. 115 | System.out.println(new Date() + " @ " + future.get()); 116 | } 117 | executor.shutdown(); 118 | } 119 | 120 | 121 | /** 122 | * This test shows another feature of Future: it can request the status of the task and thereby create nice 123 | * feedback for the user. 124 | */ 125 | @Test 126 | public void callableWrappedInFuture2() throws Exception { 127 | Callable quickCallable = () -> { 128 | Thread.sleep(1000); 129 | return Thread.currentThread().getName(); 130 | }; 131 | 132 | Callable slowCallable = () -> { 133 | Thread.sleep(2000); 134 | return Thread.currentThread().getName(); 135 | }; 136 | 137 | ExecutorService executor = Executors.newFixedThreadPool(2); 138 | Future quickTask = executor.submit(quickCallable); 139 | Future slowTask = executor.submit(slowCallable); 140 | 141 | while (true) { 142 | try { 143 | if (quickTask.isDone() && slowTask.isDone()) { 144 | System.out.println("Both tasks done!"); 145 | executor.shutdown(); 146 | return; 147 | } 148 | 149 | if (!quickTask.isDone()) { 150 | // Waiting for the first task to finish. 151 | System.out.println("quickTask output: " + quickTask.get()); 152 | } 153 | 154 | System.out.println("Waiting for slowTask to complete"); 155 | String s = slowTask.get(200L, TimeUnit.MILLISECONDS); 156 | if (s != null) { 157 | System.out.println("slowTask output: " + s); 158 | } 159 | } catch (InterruptedException | ExecutionException e) { 160 | e.printStackTrace(); 161 | } catch (TimeoutException e) { 162 | //do nothing 163 | } 164 | } 165 | } 166 | 167 | 168 | /** 169 | * Since JDK 1.8: CompletableFuture 170 | *

171 | * Since recently, we can write fluent API, for example in JavaFX and in streams. CompletableFuture lets us also 172 | * write fluent API in concurrent code. 173 | */ 174 | @Test 175 | public void CompletableFuture() throws Exception { 176 | ExecutorService executor = Executors.newFixedThreadPool(10); 177 | 178 | // Here, a task is defined. Notice that it's defined as a Supplier, not a Callable. That's necessary for the 179 | // fluent API because Supplier doesn't throw exceptions. 180 | Supplier task = () -> { 181 | try { 182 | Thread.sleep(1000); 183 | } catch (InterruptedException e) { 184 | e.printStackTrace(); 185 | } 186 | return Thread.currentThread().getName(); 187 | }; 188 | 189 | List> list = new ArrayList<>(); 190 | 191 | for (int i = 0; i < 100; i++) { 192 | CompletableFuture completableFuture = CompletableFuture.supplyAsync(task, executor); 193 | list.add(completableFuture); 194 | } 195 | 196 | for (CompletableFuture future : list) { 197 | // Here it is, our fluent API. After the CompletableFuture finished, we can just add another task to work 198 | // on. Here, it's just a println: 199 | future.thenAccept(s -> System.out.println(new Date() + " @ " + s)).get(); 200 | } 201 | 202 | executor.shutdown(); 203 | } 204 | 205 | /* 206 | Other changes in JDK 1.8: 207 | - redesign class ForkJoinPool: had just one submit queue for external tasks, now has several. Much more 208 | performance for applications with a lot of users that submit tasks simultaneously. 209 | - common pool: ForkJoinPool has new method commonPool() that returns a singleton instance of the ForkJoinPool. 210 | Used for parallel streams. 211 | - new ForkJoinTask besides existing RecursiveTask and RecursiveAction: CountedCompleter. All three classes 212 | are used for recursive programming. CountedCompleter builds a tree structure of java objects while traversing 213 | the recursion 214 | - better accumulators 215 | - new lock: StampedLock 216 | */ 217 | 218 | /* 219 | Concurrency should really be explained more in a separate tutorial and is scratched only slightly here. Hence, 220 | no exercises. 221 | */ 222 | 223 | // Repetition is a source of learning: 224 | // Why are Date and Time objects mutable? 225 | // -> Only just one question here because code samples for specific problems can be searched when used. 226 | } 227 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_01_FunctionalInterfacesAndLambdas.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.function.Consumer; 9 | import java.util.function.Function; 10 | import java.util.function.Predicate; 11 | 12 | import static org.junit.Assert.assertEquals; 13 | 14 | /** 15 | * From JavaDoc: 16 | *

17 | * "Functional interfaces provide target types for lambda expressions and method references." 18 | *

19 | * Yeah, now we know what's going on! Well, not quite yet. Let's work through this baby step by baby step. 20 | */ 21 | public class C_01_FunctionalInterfacesAndLambdas { 22 | 23 | private int instanceVariable; 24 | 25 | /** 26 | * Functional interfaces are normal interfaces but must only have abstract one method. 27 | */ 28 | @Test 29 | public void normalInterfaceOnlyOneAbstractMethod() { 30 | 31 | SimpleFunctionalInterface i = new DeepThought(); 32 | assertEquals(42, i.returnAnswerToUltimateQuestion()); 33 | 34 | // -> Functional Interface is annotated as such, but is implemented by a normal class. Boring! 35 | } 36 | 37 | /** 38 | * Functional interfaces can be implemented with Lambda expressions. 39 | */ 40 | @Test 41 | public void implementingWithLambdas() { 42 | 43 | // All right, functional interfaces must have only one abstract method. This one abstract method can be 44 | // implemented with lambdas. Yes, that is kind of cool as you will see! 45 | 46 | // Lambdas = closures = function literals = lambda expressions 47 | // Lambdas are a description of functionality that is executed later ("deferred execution") by another 48 | // part of the code. That code decides if and under which circumstances the lambda is called. That 49 | // way, the functionality can be called multiple times. 50 | 51 | SlightlyMoreSophisticatedFunctionalInterface impl = null; 52 | 53 | // Let's implement the method in various ways: 54 | 55 | impl = (int summand1, int summand2) -> (summand1 + summand2); 56 | assertEquals(3, impl.sumItUp(1, 2)); 57 | 58 | impl = (final int summand1, final int summand2) -> (summand1 + summand2); 59 | assertEquals(3, impl.sumItUp(1, 2)); 60 | 61 | 62 | impl = (summand1, summand2) -> (summand1 + summand2); 63 | assertEquals(3, impl.sumItUp(1, 2)); 64 | 65 | impl = (summand1, summand2) -> { 66 | // some much too complicated code here 67 | System.out.println("Logging stuff!"); 68 | return summand1 + summand2; 69 | }; 70 | assertEquals(3, impl.sumItUp(1, 2)); 71 | 72 | // NOPE: final without type 73 | // impl = (final summand1, final summand2) -> (summand1 + summand2); 74 | 75 | // NOPE: mixed inferred and typed argument 76 | // impl = (int summand1, summand2) -> (summand1 + summand2); 77 | 78 | 79 | /* 80 | Methods and lambdas represent a functionality. Methods however may have side effects, Lambdas don't! 81 | Lambda take input and do something and give a result back, without any side effects. See next unit test: 82 | */ 83 | } 84 | 85 | /** 86 | * Local variables used in Lambda expressions must be effectively final. 87 | */ 88 | @Test 89 | public void localVariablesHaveToBeEffectivelyFinal() { 90 | 91 | int x = 3; 92 | 93 | String string = "my string"; 94 | 95 | SlightlyMoreSophisticatedFunctionalInterface impl = (a, b) -> { 96 | 97 | // Works because the reference of this string is not changed, it stays "effectively final": 98 | String replacedString = string.replace("my", "your"); 99 | 100 | // Doesn't work because the value of the reference of the string would be changed: 101 | // string = "asdf"; 102 | 103 | // Doesn't work either because the value would be changed: 104 | // x = 5; 105 | 106 | // However, local variables can be read without problems because reading doesn't change their reference: 107 | return a + b + x; 108 | }; 109 | 110 | assertEquals(3, impl.sumItUp(1, 2)); 111 | } 112 | 113 | @Test 114 | public void lambdasHaveAccessToMembersOfTheirSurroundingClasses() { 115 | 116 | SimpleFunctionalInterface myInterfaceImpl = () -> { 117 | 118 | // Here, a lambda changes an instance variable of it's defining class: 119 | 120 | System.out.println(this.toString() + " now changing instanceVariable. Old value: " + instanceVariable); 121 | instanceVariable = (int) (Math.random() * 1000); 122 | System.out.println(this.toString() + " changed instanceVariable. New value: " + instanceVariable); 123 | 124 | // Explanation: 125 | // A Lambda becomes an instance of the class it's defined in. During construction of this instance, a 126 | // reference to the defining instance is given via constructor. Hence, Lambdas can use member variables. 127 | // If a Lambda uses local variables, they get passed into the constructor as well and are accessible also. 128 | 129 | return 0; 130 | }; 131 | 132 | System.out.println("instanceVariable = " + instanceVariable); 133 | myInterfaceImpl.returnAnswerToUltimateQuestion(); 134 | System.out.println("instanceVariable = " + instanceVariable); 135 | myInterfaceImpl.returnAnswerToUltimateQuestion(); 136 | System.out.println("instanceVariable = " + instanceVariable); 137 | } 138 | 139 | /** 140 | * Functional interfaces cannot infer functional interfaces. 141 | */ 142 | @Test 143 | public void noMultipleInheritance() { 144 | // See following interface: 145 | FunctionalInterfaceGen1 x; 146 | } 147 | 148 | // The execution of Lambdas does not generate anonymous classes. Lambdas are called with invokedynamic right at 149 | // bytecode-level. 150 | 151 | /** 152 | * Let's have a look at how Java changed in the past. Our example will be the simple iteration of a list. 153 | */ 154 | @Test 155 | public void javaTimeTravel() { 156 | 157 | // Java 1.2 158 | List list = new ArrayList(Arrays.asList(1, 2, 3)); 159 | 160 | for (int i = 0; i < list.size(); i++) { 161 | System.out.println(list.get(i)); 162 | } 163 | 164 | // Java 5 165 | List list5 = new ArrayList<>(Arrays.asList(1, 2, 3)); 166 | 167 | for (Integer i : list5) { 168 | System.out.println(i); 169 | } 170 | 171 | // Java 8 internal iteration 172 | list5.forEach(new Consumer() { 173 | @Override 174 | public void accept(Integer integer) { 175 | System.out.println(integer); 176 | } 177 | }); 178 | 179 | // Java 8 Lambdas 180 | list5.forEach(i -> System.out.println(i)); 181 | // or 182 | list5.forEach(System.out::println); 183 | } 184 | 185 | /** 186 | * To address lambdas, {@link Function} was introduced in Java 8. 187 | */ 188 | @Test 189 | public void function() { 190 | 191 | Function add37 = (x) -> x + 37; 192 | int result = add37.apply(5); // 42 193 | 194 | // Function Chaining: 195 | 196 | Function add37Duplicate = add37.andThen((x) -> x * 2); 197 | int chainResult = add37Duplicate.apply(5); 198 | } 199 | 200 | /** 201 | * A nice application for functional interfaces is the use of the new {@link java.util.function.Predicate}. A 202 | * predicate is a boolean-valued function. 203 | */ 204 | @Test 205 | public void predicate() { 206 | List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); 207 | 208 | System.out.println("Print all numbers:"); 209 | evaluate(list, (n) -> true); 210 | 211 | System.out.println("\nPrint no numbers:"); 212 | evaluate(list, (n) -> false); 213 | 214 | System.out.println("Print even numbers:"); 215 | evaluate(list, (n) -> n % 2 == 0); 216 | 217 | System.out.println("\nPrint numbers greater than 8:"); 218 | evaluate(list, (n) -> n > 8); 219 | } 220 | 221 | private void evaluate(List list, Predicate predicate) { 222 | for (Integer i : list) { 223 | if (predicate.test(i)) { 224 | System.out.print(i + " "); 225 | } 226 | } 227 | } 228 | 229 | /* 230 | Having understood this, a little party knowledge on the side: 231 | - "lambda" comes from the Lambda Calculus which simply is a formal system for computation. 232 | - introduced by Alonzo Church and Stephen Cole Kleene in the 1930s. 233 | */ 234 | } 235 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_06_DateAndTimeAPI.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import org.junit.Test; 4 | 5 | import java.time.*; 6 | import java.time.chrono.HijrahDate; 7 | import java.time.chrono.JapaneseDate; 8 | import java.time.chrono.MinguoDate; 9 | import java.time.chrono.ThaiBuddhistDate; 10 | import java.time.format.DateTimeFormatter; 11 | import java.time.temporal.*; 12 | import java.util.Date; 13 | import java.util.GregorianCalendar; 14 | import java.util.Set; 15 | 16 | /** 17 | * Because of several problems, there are alternatives to the old java.util.Date and java.util.Calendar classes. 18 | */ 19 | public class C_06_DateAndTimeAPI { 20 | 21 | /* 22 | There are two main problems with the old Date API: 23 | 1. Objects of type Date and Calendar are mutable, there always are setters. This causes them to be not 24 | thread-safe. 25 | 2. Concepts of date and time were not separated. 26 | 27 | Both issues were resolved in the new API. Here's an overview of the new classes: 28 | 29 | java.time.Instant = point in time, represented in milliseconds. Good for machines, bad for humans. 30 | java.time.LocalDate = date - human-readable, without time zone 31 | java.time.LocalTime = time - human-readable, without time zone. 32 | java.time.LocalDateTime = LocalDate + LocalTime without time zone 33 | java.time.ZonedDateTime = LocalDate + LocalTime with time zone 34 | java.time.YearMonth = year + month 35 | java.time.MonthDay = month + day 36 | java.time.Year = year 37 | 38 | The new API was strongly influenced by the Joda API (joda.org) 39 | */ 40 | 41 | @Test 42 | public void examples() { 43 | 44 | Instant instant = Instant.now(); 45 | System.out.println("Epoch second: " + instant.getEpochSecond()); 46 | LocalTime localTime = LocalTime.now(); 47 | System.out.println("LocalTime: " + localTime); 48 | LocalDate localDate = LocalDate.now(); 49 | System.out.println("LocalDate: " + localDate); 50 | LocalDateTime localDateTime = LocalDateTime.now(); 51 | System.out.println("LocalDateTime: " + localDateTime); 52 | ZonedDateTime zonedDateTime = ZonedDateTime.now(); 53 | System.out.println("ZonedDateTime: " + zonedDateTime); 54 | YearMonth yearMonth = YearMonth.now(); 55 | System.out.println("YearMonth: " + yearMonth); 56 | MonthDay monthDay = MonthDay.now(); 57 | System.out.println("MonthDay: " + monthDay); 58 | Year year = Year.now(); 59 | System.out.println("Year: " + year); 60 | } 61 | 62 | @Test 63 | public void formatting() { 64 | System.out.print(DateTimeFormatter.ofPattern("dd.MM.yyyy, HH:mm").format(LocalDateTime.now())); 65 | } 66 | 67 | @Test 68 | public void parsing() { 69 | String str = "1969-07-21 02:56"; 70 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); 71 | LocalDateTime dateTime = LocalDateTime.parse(str, formatter); 72 | System.out.println(dateTime); 73 | 74 | // General knowledge: Why did I choose that date and time? :) 75 | } 76 | 77 | /** 78 | * Often, a quick performance test is coded to measure the execution duration of code. This can be done a little bit 79 | * more easy now. 80 | */ 81 | @Test 82 | public void simpleStopWatch() throws InterruptedException { 83 | // Java 7 84 | Date begin = new Date(); 85 | Thread.sleep(2000); 86 | Date end = new Date(); 87 | System.out.println(end.getTime() - begin.getTime()); 88 | 89 | // Java 8 90 | Instant begin2 = Instant.now(); 91 | Thread.sleep(2000); 92 | Instant end2 = Instant.now(); 93 | System.out.println(Duration.between(begin2, end2).getSeconds()); 94 | } 95 | 96 | /** 97 | * Date and Time objects are immutable, i.e. they cannot be altered. If you want to express another date or time, 98 | * you have to create a new object. 99 | */ 100 | @Test 101 | public void immutable() { 102 | LocalDate birthday = LocalDate.of(2015, 7, 19); 103 | // No setter methods to change LocalDate! Only possible to create new objects like this: 104 | LocalDate birthdayIn2016 = birthday.withYear(2016); 105 | 106 | // Hence, objects are thread-safe. 107 | } 108 | 109 | /** 110 | * The new API allows to represent a day and a month independent from the year. This way, recurrent events 111 | * such as birthdays and christmas can be expressed. 112 | */ 113 | @Test 114 | public void recurrentEvents() { 115 | 116 | // Java 7: always in a specific year: 117 | GregorianCalendar d1 = new GregorianCalendar(); 118 | d1.set(GregorianCalendar.YEAR, 2015); 119 | d1.set(GregorianCalendar.MONTH, 7); 120 | d1.set(GregorianCalendar.DAY_OF_MONTH, 19); 121 | Date stevensBirthdayIn2015 = d1.getTime(); 122 | 123 | // Java 8: Stevens birthday in every year: 124 | MonthDay d2 = MonthDay.of(7, 19); 125 | 126 | // ... can be used in every year: 127 | LocalDate localDate = d2.atYear(2015); 128 | System.out.println(localDate.getDayOfWeek()); 129 | } 130 | 131 | @Test 132 | public void period() { 133 | LocalDate now = LocalDate.now(); 134 | LocalDate birthday = LocalDate.of(1984, 07, 19); 135 | 136 | Period age = Period.between(birthday, now); 137 | long ageDays = ChronoUnit.DAYS.between(birthday, now); 138 | 139 | System.out.println("I'm " + age.getYears() + " years, " + age.getMonths() + " months and " + age.getDays() 140 | + " old. That's " + ageDays + " days in total."); 141 | } 142 | 143 | @Test 144 | public void calculatingTimeAndDate() { 145 | 146 | // Simplified methods for calculating time and date and doing math: 147 | 148 | LocalTime now = LocalTime.now(); 149 | System.out.println(now); 150 | LocalTime beforeTwoHours = now.minusHours(2); 151 | System.out.println(beforeTwoHours); 152 | LocalTime inTwoHours = now.plusHours(2); 153 | System.out.println(inTwoHours); 154 | LocalTime withOneOClock = now.withHour(1); 155 | System.out.println(withOneOClock); 156 | 157 | LocalDate localDate = LocalDate.now(); 158 | System.out.println(localDate); 159 | LocalDate beforeTwoDays = localDate.minusDays(2); 160 | System.out.println(beforeTwoDays); 161 | LocalDate inTwoDays = localDate.plusDays(2); 162 | System.out.println(inTwoDays); 163 | LocalDate withFirstDayOfMonth = localDate.withDayOfMonth(1); 164 | System.out.println(withFirstDayOfMonth); 165 | } 166 | 167 | @Test 168 | public void temporalAdjuster() { 169 | 170 | // temporal adjuster = implementation of strategy design pattern for "modifying" a temporal object, i.e. 171 | // creating a new one. 172 | 173 | LocalDateTime now = LocalDateTime.now(); 174 | System.out.println("Last day of year: " + now.with(TemporalAdjusters.lastDayOfYear())); 175 | System.out.println("First day of next year: " + now.with(TemporalAdjusters.firstDayOfNextYear())); 176 | 177 | TemporalAdjuster nextOddDayTemporalAdjuster = new TemporalAdjuster() { 178 | @Override 179 | public Temporal adjustInto(Temporal temporal) { 180 | LocalDate localDate = LocalDate.from(temporal); 181 | 182 | int day = localDate.getDayOfMonth(); 183 | if (day % 2 == 0) { 184 | localDate = localDate.plusDays(1); 185 | } else { 186 | localDate = localDate.plusDays(2); 187 | } 188 | 189 | return temporal.with(localDate); 190 | } 191 | }; 192 | 193 | System.out.println("Next odd day: " + now.with(nextOddDayTemporalAdjuster)); 194 | } 195 | 196 | @Test 197 | public void timezones() { 198 | /* 199 | Java 7: 200 | java.util.TimeZone 201 | = designator ("Europe/Berlin") 202 | + Offset to Greenwich/UTC-time ("+02:00") 203 | + rules for when summer and winter time change. 204 | 205 | Java 8: separation of concerns: 206 | ZoneId + ZoneOffset + ZoneRules 207 | */ 208 | 209 | ZoneId losAngeles = ZoneId.of("America/Los_Angeles"); 210 | ZoneId berlin = ZoneId.of("Europe/Berlin"); 211 | LocalDateTime dateTime = LocalDateTime.of(2014, 02, 20, 12, 0); 212 | ZonedDateTime berlinDateTime = ZonedDateTime.of(dateTime, berlin); 213 | ZonedDateTime losAngelesDateTime = berlinDateTime.withZoneSameInstant(losAngeles); 214 | 215 | int offsetInSeconds = losAngelesDateTime.getOffset().getTotalSeconds(); 216 | Set allZoneIds = ZoneId.getAvailableZoneIds(); 217 | LocalDateTime date = LocalDateTime.of(2013, Month.JULY, 20, 3, 30); 218 | ZoneOffset offset = ZoneOffset.of("+05:00"); 219 | OffsetDateTime plusFive = OffsetDateTime.of(date, offset); 220 | OffsetDateTime minusTwo = plusFive.withOffsetSameInstant(ZoneOffset.ofHours(-2)); 221 | } 222 | 223 | @Test 224 | public void calendarSystems() { 225 | LocalDateTime ldt = LocalDateTime.now(); 226 | HijrahDate hdate = HijrahDate.from(ldt); 227 | JapaneseDate jdate = JapaneseDate.from(ldt); 228 | MinguoDate mdate = MinguoDate.from(ldt); 229 | ThaiBuddhistDate tdate = ThaiBuddhistDate.from(ldt); 230 | 231 | System.out.println(" Today: " + DateTimeFormatter.ofPattern("dd.MM.yyyy, HH:mm").format(ldt) + "\n Hijrah date: " + hdate 232 | + "\n Japanese date: " + jdate + "\n Minguo date: " + mdate 233 | + "\n ThaiBuddhist date: " + tdate); 234 | } 235 | 236 | 237 | /* 238 | Behold: Currently, there is no JPA / Hibernate support for this new API, see 239 | https://hibernate.atlassian.net/browse/HHH-8844 240 | Use a JPA attribute converter or Hibernate user types. 241 | 242 | More information at http://java.dzone.com/articles/deeper-look-java-8-date-and 243 | */ 244 | 245 | } 246 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_04_Streams.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.nio.file.DirectoryStream; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.util.*; 11 | import java.util.concurrent.atomic.AtomicInteger; 12 | import java.util.function.Supplier; 13 | import java.util.regex.Pattern; 14 | import java.util.stream.*; 15 | 16 | import static org.junit.Assert.assertTrue; 17 | 18 | /** 19 | * Stream is not a data structure, it doesn't hold data. It just holds references to the underlying stream 20 | * source and knows a number of tasks that should be executed against each element of the stream source. Streams are 21 | * pipelines that handle data structures to operations. 22 | *

23 | * Streams = intermediate operations (filter, map) + terminal operation (reduce, sum) 24 | */ 25 | public class C_04_Streams { 26 | 27 | @Test 28 | public void creatingStreams() { 29 | 30 | // 1. collection-based 31 | String[] stringArray = {"first", "second", "third", "fourth"}; 32 | List stringList = Arrays.asList(stringArray); 33 | 34 | // stream = "do with every element in the given order" 35 | // parallel stream = "do with every element in a random order but much faster" 36 | Stream streamFromCollection = stringList.stream(); 37 | Stream parallelStreamFromCollection = stringList.parallelStream(); 38 | 39 | // 2. array-based 40 | Stream streamFromArray = Arrays.stream(stringArray); 41 | Stream parallelStreamFromArray = Arrays.stream(stringArray).parallel(); 42 | 43 | Stream streamFromStaticArrayMethod = Stream.of("first", "second", "third", "fourth"); 44 | 45 | // 3.1 generate via supplier 46 | Stream randomNumberStream = Stream.generate(Math::random); 47 | Stream integerStream1 = Stream.generate(new AtomicInteger()::getAndIncrement); 48 | 49 | // 3.2 generate via seed + operator 50 | Stream integerStream2 = Stream.iterate(0, integer1 -> integer1 + 1); 51 | } 52 | 53 | @Test 54 | public void neverEndingStream() { 55 | 56 | // Supplier is a new functional interface that somehow generates values or objects. 57 | Supplier r = Math::random; 58 | 59 | // This stream has no intermediate operations and one terminal operation (println): 60 | Stream.generate(r).forEach(System.out::println); 61 | 62 | // System.out::println is a terminal operation. That's why the streams in the test above are not executed but 63 | // this stream here is. 64 | } 65 | 66 | @Test 67 | public void intermediateAndTerminalOperation() { 68 | 69 | // This stream has two intermediate operations and one terminal operation (println): 70 | System.out.println("First stream:"); 71 | Stream stream1 = Stream.generate(Math::random); 72 | stream1.limit(3).sorted().forEach(System.out::println); 73 | stream1.close(); 74 | 75 | // That is the same as above with a lambda expression: 76 | System.out.println("\n2nd stream:"); 77 | Stream stream2 = Stream.generate(Math::random); 78 | stream2.limit(3).sorted().forEach((x) -> System.out.println(x)); 79 | stream2.close(); 80 | } 81 | 82 | @Test 83 | public void primitiveStreams() { 84 | 85 | // There are special streams for some primitive types: int, long and double. 86 | // Streams more efficient than Stream because boxing/unboxing not done. 87 | // For int and long there are Special range-methods: 88 | IntStream efficientIntStream = IntStream.range(0, 4); 89 | Stream inefficientIntStream = Stream.of(0, 1, 2, 3); 90 | 91 | LongStream efficientLongStream = LongStream.range(0L, 4L); 92 | Stream inefficientLongStream = Stream.of(0L, 1L, 2L, 3L); 93 | 94 | DoubleStream doubleStream = DoubleStream.of(0.0d, 0.5d); 95 | 96 | // (Streams intentionally not closed here) 97 | } 98 | 99 | @Test 100 | public void regexStreams() { 101 | String string = "This is just a random test string!"; 102 | Stream stringStream = Pattern.compile("\\W").splitAsStream(string); 103 | stringStream.forEach(System.out::println); 104 | stringStream.close(); 105 | } 106 | 107 | @Test 108 | public void collect() { 109 | Stream stream = Stream.of("one", "two", "three"); 110 | Set stringSet = stream.collect(Collectors.toSet()); 111 | assertTrue(stringSet.contains("one")); 112 | assertTrue(stringSet.contains("two")); 113 | assertTrue(stringSet.contains("three")); 114 | 115 | stream = Stream.of("one", "two", "three"); 116 | String joined = stream.collect(Collectors.joining(", ")); 117 | stream.close(); 118 | System.out.println(joined); 119 | } 120 | 121 | @Test 122 | public void fileStreams() throws IOException { 123 | DirectoryStream directoryStream = Files.newDirectoryStream(new File("src/main/java").toPath()); 124 | directoryStream.forEach(System.out::println); 125 | directoryStream.close(); 126 | 127 | Stream linesStream = Files.lines(new File("src/main/java/de.stevenschwenke.java.java8workshop.DeepThought.java").toPath()); 128 | linesStream.forEach(System.out::println); 129 | linesStream.close(); 130 | 131 | // Line by line reading is done without reading the whole file into memory, see http://java.dzone.com/articles/understanding-java-8-streams-1 : 132 | // "Collections are in-memory data structures which hold elements within it. Each element in the collection is computed before it actually becomes a part of that collection. On the other hand Streams are fixed data structures which computes the elements on-demand basis. 133 | // The Java 8 Streams can be seen as lazily constructed Collections, where the values are computed when user demands for it. Actual Collections behave absolutely opposite to it and they are set of eagerly computed values (no matter if the user demands for a particular value or not)." 134 | // and http://winterbe.com/posts/2015/03/25/java8-examples-string-number-math-files/ : 135 | // "As an memory-efficient alternative you could use the method Files.lines. Instead of reading all lines into memory at once, this method reads and streams each line one by one via functional streams." 136 | } 137 | 138 | @Test 139 | public void parallelStreamsRunMultiThreaded() { 140 | List stringList = Arrays.asList("first", "second", "third", "fourth"); 141 | 142 | Stream parallelStream = stringList.parallelStream(); 143 | parallelStream.forEach(System.out::println); 144 | parallelStream.close(); 145 | 146 | // Advanced configuration of parallel streams via custom thread pool, 147 | // see http://stackoverflow.com/questions/21163108/custom-thread-pool-in-java-8-parallel-stream 148 | } 149 | 150 | @Test 151 | public void multiThreadPerformance() { 152 | 153 | // This test is a playground for testing performance between calculating sequential and parallel sum of a 154 | // long double stream. Play with the length of the stream: 155 | int lengthOfStream = 2000; 156 | 157 | List randomDoubleList = new ArrayList<>(); 158 | for (int i = 0; i < lengthOfStream; i++) { 159 | randomDoubleList.add(Math.random()); 160 | } 161 | 162 | // 1. calculating the sum with a sequential stream 163 | long start = System.currentTimeMillis(); 164 | Stream sequentialStream = randomDoubleList.stream(); 165 | Double sumSequential = sequentialStream.reduce((aDouble, aDouble2) -> aDouble + aDouble2).get(); 166 | long end = System.currentTimeMillis(); 167 | long durationSequential = end - start; 168 | System.out.println("Sequential calculated sum = " + sumSequential); 169 | System.out.println("Calculated in " + durationSequential + " ms"); 170 | sequentialStream.close(); 171 | 172 | // 2. calculating the sum with a parallel stream 173 | start = System.currentTimeMillis(); 174 | Stream parallelStream = randomDoubleList.parallelStream(); 175 | Double sumParallel = parallelStream.reduce((aDouble, aDouble2) -> aDouble + aDouble2).get(); 176 | end = System.currentTimeMillis(); 177 | long durationParallel = end - start; 178 | System.out.println("Parallel calculated sum = " + sumParallel); 179 | System.out.println("Calculated in " + durationParallel + " ms"); 180 | parallelStream.close(); 181 | 182 | // Hint: rounding error because of addition 183 | 184 | // QUESTION: Why can we use the list of random doubles here again - shouldn't it be manipulated by the 185 | // operations above? 186 | 187 | 188 | // ANSWER from question above: Input parameters of streams are not changed by stream operations. The list 189 | // is just the same so we can use it again. 190 | 191 | // CONCLUSION: 192 | // Runtime with different length of stream very different, dependent on the machine. Sometimes even the 193 | // sequential stream is faster. 194 | } 195 | 196 | @Test 197 | public void splittableRandom() { 198 | // New class for creating random numbers, that additionally supports streams. To support parallel streams, 199 | // numbers that are generated in parallel threads should be independent from each other. In other words: 200 | // this generator is not "shared" between threads, it's "splitted". Also, it's faster then Math.random(). :) 201 | 202 | DoubleStream randomStreamWithThreeDoubles = new SplittableRandom().doubles(3); 203 | DoubleStream threeRandomNumbersBetween0And100 = new SplittableRandom().doubles(3, 0, 100); 204 | // actually, the above is [0, 100) = including 0 and < 100 205 | } 206 | 207 | @Test 208 | public void alteringDataSourceOfAStream() { 209 | Queue q = new LinkedList<>(); 210 | q.add("1"); 211 | q.add("2"); 212 | q.stream().peek(x -> { 213 | System.out.println(x); 214 | q.add(("NEW")); // ConcurrentModificationException 215 | }).count(); 216 | } 217 | 218 | @Test 219 | public void nonInterference() { 220 | // Although the elements of the underlying collection should not be changed by a stream, it sure is possible, 221 | // see the code below. However, see http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#NonInterference, 222 | // " For most data sources, preventing interference means ensuring that the data source is not modified at all 223 | // during the execution of the stream pipeline" 224 | 225 | SimpleClass simpleClass1 = new SimpleClass(1); 226 | System.out.println(simpleClass1.getX()); // "1" 227 | 228 | Stream stream = Stream.of(simpleClass1); 229 | stream.peek(x -> { 230 | x.setX(42); 231 | }).forEach(System.out::println); // "42" 232 | 233 | System.out.println(simpleClass1.getX()); // "42" -> the underlying object has been altered by the stream! 234 | } 235 | 236 | public class SimpleClass { 237 | private int x; 238 | 239 | public SimpleClass(int x) { 240 | this.x = x; 241 | } 242 | 243 | public int getX() { 244 | return x; 245 | } 246 | 247 | public void setX(int x) { 248 | this.x = x; 249 | } 250 | 251 | @Override 252 | public String toString() { 253 | return ""+x; 254 | } 255 | } 256 | 257 | 258 | /* 259 | 260 | ... and many more streams at java doc for java.util.stream 261 | http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html 262 | 263 | Important side note: Parallel streams are supposed to be faster. However, there are pitfalls. Also, intelligent 264 | people suggested to never use it because of thread issues. Read into that at 265 | http://java.dzone.com/articles/think-twice-using-java-8 266 | http://zeroturnaround.com/rebellabs/java-parallel-streams-are-bad-for-your-health/ 267 | */ 268 | } 269 | -------------------------------------------------------------------------------- /src/test/java/de/stevenschwenke/java/java8workshop/C_09_JavaFX.java: -------------------------------------------------------------------------------- 1 | package de.stevenschwenke.java.java8workshop; 2 | 3 | import javafx.application.Application; 4 | import javafx.beans.binding.Bindings; 5 | import javafx.beans.property.ReadOnlyStringWrapper; 6 | import javafx.beans.property.SimpleBooleanProperty; 7 | import javafx.collections.FXCollections; 8 | import javafx.collections.ObservableList; 9 | import javafx.collections.transformation.FilteredList; 10 | import javafx.concurrent.ScheduledService; 11 | import javafx.concurrent.Task; 12 | import javafx.print.*; 13 | import javafx.scene.Scene; 14 | import javafx.scene.control.*; 15 | import javafx.scene.layout.HBox; 16 | import javafx.scene.layout.VBox; 17 | import javafx.scene.paint.Color; 18 | import javafx.scene.text.*; 19 | import javafx.scene.transform.Scale; 20 | import javafx.stage.Stage; 21 | import javafx.util.Duration; 22 | import javafx.util.StringConverter; 23 | import org.junit.Test; 24 | 25 | import java.time.LocalDate; 26 | import java.time.format.DateTimeFormatter; 27 | import java.util.concurrent.Executors; 28 | 29 | import static javafx.beans.binding.Bindings.when; 30 | import static org.junit.Assert.assertEquals; 31 | 32 | /** 33 | * The official Java GUI framework, JavaFX, also got some new features with Java 8. For a more 34 | * detailed workshop, have a look at https://github.com/stevenschwenke/JavaFXWorkshop. 35 | */ 36 | public class C_09_JavaFX extends Application { 37 | 38 | /* 39 | Naming: 40 | Java 7 -> "JavaFX 2.x" 41 | Java 8 -> "JavaFX 8" 42 | */ 43 | 44 | 45 | // new properties and methods annotated with 46 | // @since JavaFX 8.0 47 | 48 | /** 49 | * New component: DatePicker (finally!) that uses new Date and Time API. 50 | * Also: TransformationList -> FilteredList + OrderedList 51 | */ 52 | @Override 53 | public void start(Stage primaryStage) { 54 | VBox root = new VBox(); 55 | root.setSpacing(15); 56 | 57 | // DatePicker 58 | 59 | DatePicker datePicker = new DatePicker(); 60 | datePicker.setShowWeekNumbers(true); 61 | setFormattingToGermanDateFormat(datePicker); 62 | setSomeForeignChronologies(datePicker); 63 | 64 | Button btn = new Button(); 65 | btn.setText("getValue()"); 66 | // Note that output in the console is default formatting, not formatting of the DatePicker! 67 | btn.setOnAction(event -> System.out.println(datePicker.getValue())); 68 | 69 | HBox datePickerContainer = new HBox(); 70 | datePickerContainer.setSpacing(10); 71 | datePickerContainer.getChildren().add(datePicker); 72 | datePickerContainer.getChildren().add(btn); 73 | 74 | root.getChildren().add(new VBox(createTextFlowLabel("DatePicker"), datePickerContainer)); 75 | 76 | // treetableview 77 | final TreeItem childNode1 = new TreeItem<>("Child Node 1"); 78 | final TreeItem childNode2 = new TreeItem<>("Child Node 2"); 79 | final TreeItem childNode3 = new TreeItem<>("Child Node 3"); 80 | final TreeItem rootTreeItem = new TreeItem<>("Root node"); 81 | rootTreeItem.setExpanded(true); 82 | rootTreeItem.getChildren().setAll(childNode1, childNode2, childNode3); 83 | TreeTableColumn column = new TreeTableColumn<>("Column"); 84 | column.setPrefWidth(150); 85 | column.setCellValueFactory((TreeTableColumn.CellDataFeatures p) -> 86 | new ReadOnlyStringWrapper(p.getValue().getValue())); 87 | 88 | final TreeTableView treeTableView = new TreeTableView<>(rootTreeItem); 89 | treeTableView.getColumns().add(column); 90 | treeTableView.setPrefWidth(152); 91 | treeTableView.setMinHeight(130); 92 | treeTableView.setPrefHeight(130); 93 | treeTableView.setShowRoot(true); 94 | root.getChildren().add(new VBox(createTextFlowLabel("TreeTableView"), treeTableView)); 95 | 96 | // ListFiltering 97 | 98 | // New TransformationList is a wrapper around a normal list and has two implementations: FilteredList and 99 | // SortedList. The following is an example for the FilteredList, SortedList is similar. 100 | 101 | ObservableList list = FXCollections.observableArrayList("one", "two", "three", "four"); 102 | FilteredList filteredList = new FilteredList<>(list); 103 | ListView listView = new ListView<>(filteredList); 104 | TextField textField = new TextField(); 105 | textField.textProperty().addListener((e) -> filteredList.setPredicate((v) -> (v.contains(textField.getText())))); 106 | VBox listFilteringContainer = new VBox(createTextFlowLabel("ListFiltering"), textField, listView); 107 | 108 | root.getChildren().add(listFilteringContainer); 109 | 110 | // Task: updateValue 111 | 112 | Task task = new Task() { 113 | @Override 114 | protected String call() throws Exception { 115 | long startTime = System.currentTimeMillis(); 116 | while (true) { 117 | updateValue(System.currentTimeMillis() - startTime + ""); 118 | Thread.sleep(1); 119 | } 120 | } 121 | }; 122 | Button button = new Button(); 123 | button.setOnAction((e) -> { 124 | if (task.isRunning()) { 125 | task.cancel(); 126 | } else { 127 | // The executor will execute the task only when it didn't run yet. Additional calls will be ignored. 128 | Executors.newSingleThreadExecutor().execute(task); 129 | } 130 | }); 131 | button.textProperty().bind(when(task.valueProperty().isNotNull()).then(task.valueProperty()).otherwise("Start")); 132 | VBox threadContainer = new VBox(createTextFlowLabel("Task.updateValue()"), button); 133 | root.getChildren().add(threadContainer); 134 | 135 | // new class: Scheduled Service 136 | 137 | ScheduledService service = new ScheduledService() { 138 | @Override 139 | protected Task createTask() { 140 | return new Task() { 141 | @Override 142 | protected Void call() throws Exception { 143 | System.out.println("Do something"); 144 | return null; 145 | } 146 | }; 147 | } 148 | }; 149 | service.setRestartOnFailure(true); 150 | service.setPeriod(Duration.seconds(5)); 151 | service.setMaximumFailureCount(10); 152 | service.setMaximumCumulativePeriod(Duration.minutes(2)); 153 | Button startScheduledService = new Button("Start scheduled service"); 154 | startScheduledService.setOnAction(eventHandler -> service.start()); 155 | root.getChildren().add(new VBox(createTextFlowLabel("ScheduledService"), startScheduledService)); 156 | 157 | // New CSS theme Modena is new default theme! 158 | Button toggleThemes = new Button(); 159 | SimpleBooleanProperty modena = new SimpleBooleanProperty(true); 160 | toggleThemes.textProperty().bind(Bindings.when(modena).then("Switch to Caspian").otherwise("Switch to Modena")); 161 | toggleThemes.setOnAction(eventHandler -> { 162 | setUserAgentStylesheet(modena.get() ? STYLESHEET_CASPIAN : STYLESHEET_MODENA); 163 | modena.set(getUserAgentStylesheet().equals(STYLESHEET_MODENA)); 164 | }); 165 | // (If getUserAgendStylesheet() would be a property, the above code would be way smaller.) 166 | root.getChildren().add(new VBox(createTextFlowLabel("toggle themes"), toggleThemes)); 167 | 168 | // Print support 169 | Button print = new Button("Print"); 170 | print.setOnAction(eventHandler -> { 171 | Printer printer = Printer.getDefaultPrinter(); 172 | PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.PORTRAIT, Printer.MarginType.DEFAULT); 173 | double scaleX = pageLayout.getPrintableWidth() / root.getBoundsInParent().getWidth(); 174 | double scaleY = pageLayout.getPrintableHeight() / root.getBoundsInParent().getHeight(); 175 | Scale printScale = new Scale(scaleX, scaleY); 176 | root.getTransforms().add(printScale); 177 | 178 | PrinterJob job = PrinterJob.createPrinterJob(); 179 | if(job != null) { 180 | boolean success = job.printPage(root); 181 | if(success) { 182 | job.endJob(); 183 | } 184 | } 185 | 186 | // Don't forget to remove printer scaling here because otherwise your app looks bad! 187 | root.getTransforms().remove(printScale); 188 | }); 189 | root.getChildren().add(new VBox(createTextFlowLabel("Print support"), print)); 190 | 191 | // Dialogs 192 | Button dialogs = new Button("Dialogs"); 193 | dialogs.setOnAction(eventHandler -> { 194 | Alert alert = new Alert(Alert.AlertType.INFORMATION); 195 | alert.setTitle("There are dialogs!"); 196 | alert.setHeaderText(null); 197 | alert.setContentText("Dialogs have been imported from ControlsFX and can now be used with default Java."); 198 | 199 | alert.showAndWait(); 200 | 201 | // More examples here: http://code.makery.ch/blog/javafx-dialogs-official/ 202 | }); 203 | root.getChildren().add(new VBox(createTextFlowLabel("Dialogs"), dialogs)); 204 | 205 | // JAVAFX 3D 206 | // See https://www.youtube.com/watch?v=TS5RvqDsEoU (JavaFX 3D and Leap Motion: a short space adventure ) 207 | // See https://www.youtube.com/watch?v=8_xiv1pV3tI (Rigged Hand Animation with JavaFX and Leap Motion ) 208 | 209 | // Setup GUI 210 | 211 | Scene scene = new Scene(root, 300, 680); 212 | primaryStage.setTitle("JavaFX in Java 8"); 213 | primaryStage.setScene(scene); 214 | primaryStage.show(); 215 | } 216 | 217 | /** 218 | * Creates a nice-looking label with the new {@link TextFlow} component. It can also display images! 219 | * 220 | * @param text for the label 221 | * @return {@link TextFlow} component that can be added as a child 222 | */ 223 | private TextFlow createTextFlowLabel(String text) { 224 | Text datePickerLabelText = new Text(text); 225 | datePickerLabelText.setFill(Color.BLUE); 226 | datePickerLabelText.setFont(Font.font("Helvetica", FontWeight.BOLD, 15)); 227 | return new TextFlow(datePickerLabelText); 228 | } 229 | 230 | /** 231 | * Sets the formatting of a {@link javafx.scene.control.DatePicker} to the german date format "dd.MM.yyyy". 232 | * 233 | * @param datePicker to set format for 234 | */ 235 | private void setFormattingToGermanDateFormat(DatePicker datePicker) { 236 | // Convert date in text field manually: 237 | String pattern = "dd.MM.yyyy"; 238 | datePicker.setPromptText(pattern.toLowerCase()); 239 | 240 | datePicker.setConverter(new StringConverter() { 241 | DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(pattern); 242 | 243 | @Override 244 | public String toString(LocalDate date) { 245 | if (date != null) { 246 | return dateFormatter.format(date); 247 | } else { 248 | return ""; 249 | } 250 | } 251 | 252 | @Override 253 | public LocalDate fromString(String string) { 254 | if (string != null && !string.isEmpty()) { 255 | return LocalDate.parse(string, dateFormatter); 256 | } else { 257 | return null; 258 | } 259 | } 260 | }); 261 | } 262 | 263 | /** 264 | * Shows the support of {@link javafx.scene.control.DatePicker} for different cultural time systems. 265 | * 266 | * @param datePicker to set chronology for 267 | */ 268 | private void setSomeForeignChronologies(DatePicker datePicker) { 269 | // Japanese calendar. 270 | // datePicker.setChronology(JapaneseChronology.INSTANCE); 271 | 272 | // Hijrah calendar. 273 | // datePicker.setChronology(HijrahChronology.INSTANCE); 274 | 275 | // Minguo calendar. 276 | // datePicker.setChronology(MinguoChronology.INSTANCE); 277 | 278 | // Buddhist calendar. 279 | // datePicker.setChronology(ThaiBuddhistChronology.INSTANCE); 280 | } 281 | 282 | 283 | @Test(expected = Exception.class) 284 | public void implementationsOfTransformationListAreImmutable() { 285 | ObservableList list = FXCollections.observableArrayList("one", "two", "three", "four"); 286 | FilteredList filteredList = new FilteredList<>(list); 287 | 288 | // Backing list can be mutated: 289 | list.remove(0, 1); 290 | assertEquals(3, list.size()); 291 | 292 | // All implementations of TransformationList are immutable, hence changing the list will throw an exception: 293 | filteredList.add("EXCEPTION!"); 294 | } 295 | 296 | /* 297 | I cannot be sure that everyone of the attendees is keen in writing JavaFX code. Hence, no exercises. 298 | */ 299 | 300 | // Repetition is a source of learning: 301 | // What is the main change for annotations in Java 8? 302 | // Can one position of code be annotated with a specific annotation more than once? 303 | } 304 | --------------------------------------------------------------------------------