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