├── .gitignore ├── 01-default-static-interface-methods.md ├── 02-lambdas.md ├── 03-streams.md ├── 04-collectors.md ├── 05-optionals.md ├── 06-map.md ├── 07-building-functional-programs.md ├── 08-date-time-api.md ├── 09-completable-futures.md ├── 10-nashorn.md ├── 11-tools.md ├── 12-annotations.md ├── LICENSE ├── README.md ├── ch10 └── lines.js ├── code ├── .gitignore ├── README.md ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── main │ └── java │ │ └── com │ │ └── shekhargulati │ │ └── java8_tutorial │ │ ├── ch01 │ │ ├── App1.java │ │ ├── App2.java │ │ ├── App3.java │ │ ├── BasicCalculator.java │ │ ├── Calculator.java │ │ └── CalculatorFactory.java │ │ ├── ch02 │ │ ├── Example1_Lambda.java │ │ ├── Example2_Lambda.java │ │ ├── Example3_Functionalnterfaces.java │ │ ├── Example4_MethodReferences.java │ │ └── InvalidFunctionInterface.java │ │ ├── ch03 │ │ ├── Example1_Java7.java │ │ ├── Example1_Stream.java │ │ ├── Examples.java │ │ └── ParallelStreamExample.java │ │ ├── ch05 │ │ ├── TaskNotFoundException.java │ │ ├── TaskRepository.java │ │ └── domain │ │ │ ├── Task.java │ │ │ └── User.java │ │ ├── ch06 │ │ └── MapExample.java │ │ ├── ch09 │ │ └── CompletableFutureExample.java │ │ ├── ch10 │ │ ├── NashornExample1.java │ │ ├── NashornExample2.java │ │ └── NashornExample3.java │ │ ├── ch12 │ │ ├── CreateVm.java │ │ ├── MyAnnotation.java │ │ └── VmManager.java │ │ ├── domain │ │ ├── Task.java │ │ └── TaskType.java │ │ └── utils │ │ └── DataUtils.java │ └── test │ └── java │ └── com │ └── shekhargulati │ └── java8_tutorial │ ├── ch01 │ └── CalculatorTest.java │ └── ch06 │ └── MapExampleTest.java ├── es ├── 01-default-static-interface-methods.md ├── 02-lambdas.md ├── 03-streams.md ├── 04-collectors.md ├── 05-optionals.md ├── 06-map.md ├── 07-building-functional-programs.md ├── 08-date-time-api.md ├── 09-completable-futures.md ├── 10-nashorn.md ├── 11-tools.md ├── 12-annotations.md ├── LICENSE └── README.md └── java8-slides.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | -------------------------------------------------------------------------------- /01-default-static-interface-methods.md: -------------------------------------------------------------------------------- 1 | Default and Static Methods for Interfaces [![TimeToRead](http://ttr.myapis.xyz/ttr.svg?pageUrl=https://github.com/shekhargulati/java8-the-missing-tutorial/blob/master/01-default-static-interface-methods.md)](http://ttr.myapis.xyz/) 2 | -------- 3 | 4 | We all understand that we should code to interfaces. Interfaces give the client 5 | a contract which they should use without relying on implementation details (i.e. 6 | classes). Hence, promoting **[loose coupling](https://en.wikipedia.org/wiki/Loose_coupling)**. 7 | Designing clean interfaces is one of the most important aspect of API design. 8 | One of the SOLID principle **[Interface segregation](https://en.wikipedia.org/wiki/Interface_segregation_principle)** 9 | talks about designing smaller client-specific interfaces, instead of designing 10 | one general purpose interface. Interface design is the key to clean and 11 | effective APIs for your libraries and applications. 12 | 13 | > Code for this section is inside [ch01 package](https://github.com/shekhargulati/java8-the-missing-tutorial/tree/master/code/src/main/java/com/shekhargulati/java8_tutorial/ch01). 14 | 15 | If you have designed any API then with time you would have felt the need to add 16 | new methods to the API. Once an API is published, it becomes difficult to add 17 | methods to an interface without breaking existing implementations. To make this 18 | point clear, suppose you are building a simple `Calculator` API that supports 19 | `add`,`subtract`, `divide`, and `multiply` operations. We can write a 20 | `Calculator` interface, as shown below. ***To keep things simple we will use 21 | `int`.*** 22 | 23 | ```java 24 | public interface Calculator { 25 | 26 | int add(int first, int second); 27 | 28 | int subtract(int first, int second); 29 | 30 | int divide(int number, int divisor); 31 | 32 | int multiply(int first, int second); 33 | } 34 | ``` 35 | 36 | To back this `Calculator` interface, you created a `BasicCalculator` 37 | implementation, as shown below. 38 | 39 | ```java 40 | public class BasicCalculator implements Calculator { 41 | 42 | @Override 43 | public int add(int first, int second) { 44 | return first + second; 45 | } 46 | 47 | @Override 48 | public int subtract(int first, int second) { 49 | return first - second; 50 | } 51 | 52 | @Override 53 | public int divide(int number, int divisor) { 54 | if (divisor == 0) { 55 | throw new IllegalArgumentException("divisor can't be zero."); 56 | } 57 | return number / divisor; 58 | } 59 | 60 | @Override 61 | public int multiply(int first, int second) { 62 | return first * second; 63 | } 64 | } 65 | ``` 66 | 67 | ## Static Factory Methods 68 | 69 | Suppose the Calculator API turned out to be very useful and easy to use. Users 70 | just have to create an instance of `BasicCalculator`, and then they can use the 71 | API. You start seeing code like that shown below. 72 | 73 | ```java 74 | Calculator calculator = new BasicCalculator(); 75 | int sum = calculator.add(1, 2); 76 | 77 | BasicCalculator cal = new BasicCalculator(); 78 | int difference = cal.subtract(3, 2); 79 | ``` 80 | 81 | Oh no! Users of the API are not coding to `Calculator` interface -- instead, 82 | they are coding to its implementation. Your API didn't enforce users to code to 83 | interfaces, as the `BasicCalculator` class was public. If you make 84 | `BasicCalculator` package protected, then you would have to provide a static 85 | factory class that will take care of providing the `Calculator` implementation. 86 | Let's improve the code to handle this. 87 | 88 | First, we will make `BasicCalculator` package protected so that users can't 89 | access the class directly. 90 | 91 | ```java 92 | class BasicCalculator implements Calculator { 93 | // rest remains same 94 | } 95 | ``` 96 | 97 | Next, we will write a factory class that will give us the `Calculator` instance, 98 | as shown below. 99 | 100 | ```java 101 | public abstract class CalculatorFactory { 102 | 103 | public static Calculator getInstance() { 104 | return new BasicCalculator(); 105 | } 106 | } 107 | ``` 108 | 109 | Now, users will be forced to code to the `Calculator` interface, and they will 110 | not have access to implementation details. 111 | 112 | Although we have achieved our goal, we have increased the surface area of our 113 | API by adding the new class `CalculatorFactory`. Now users of the API have to 114 | learn about one more class before they can use the API effectively. This was the 115 | only solution available before Java 8. 116 | 117 | **Java 8 allows you to declare static methods inside an interface**. This allows 118 | API designers to define static utility methods like `getInstance` in the 119 | interface itself, hence keeping the API short and lean. The static methods 120 | inside an interface could be used to replace static helper classes 121 | (`CalculatorFactory`) that we normally create to define helper methods 122 | associated with a type. For example, the `Collections` class is a helper class 123 | that defines various helper methods to work with Collection and its associated 124 | interfaces. The methods defined in the `Collections` class could easily be added 125 | to `Collection` or any of its child interfaces. 126 | 127 | The above code can be improved in Java 8 by adding a static `getInstance` method 128 | in the `Calculator` interface itself. 129 | 130 | ```java 131 | public interface Calculator { 132 | 133 | static Calculator getInstance() { 134 | return new BasicCalculator(); 135 | } 136 | 137 | int add(int first, int second); 138 | 139 | int subtract(int first, int second); 140 | 141 | int divide(int number, int divisor); 142 | 143 | int multiply(int first, int second); 144 | 145 | } 146 | ``` 147 | 148 | ## Evolving API with time 149 | 150 | Some of the consumers decided to either extend the `Calculator` API by adding 151 | methods like `remainder`, or write their own implementation of the `Calculator` 152 | interface. After talking to your users you came to know that most of them would 153 | like to have a `remainder` method added to the `Calculator` interface. It looked 154 | a very simple API change, so you added one more method to the API. 155 | 156 | ```java 157 | public interface Calculator { 158 | 159 | static Calculator getInstance() { 160 | return new BasicCalculator(); 161 | } 162 | 163 | int add(int first, int second); 164 | 165 | int subtract(int first, int second); 166 | 167 | int divide(int number, int divisor); 168 | 169 | int multiply(int first, int second); 170 | 171 | int remainder(int number, int divisor); // new method added to API 172 | } 173 | ``` 174 | 175 | Adding a method to an interface broke the source compatibility of the API. This 176 | means users who were implementing `Calculator` interface would have to add 177 | implementation for the `remainder` method, otherwise their code would not 178 | compile. This is a big problem for API designers, as it makes APIs difficult to 179 | evolve. Prior to Java 8, it was not possible to have method implementations 180 | inside interfaces. This often became a problem when it was required to extend an 181 | API, i.e. adding one or more methods to the interface definition. 182 | 183 | To allow API's to evolve with time, Java 8 allows users to provide default 184 | implementations to methods defined in the interface. These are called 185 | **default**, or **defender** methods. The class implementing the interface is not 186 | required to provide an implementation of these methods. If an implementing class 187 | provides the implementation, then the implementing class method implementation 188 | will be used -- otherwise the default implementation will be used. The `List` 189 | interface has a few default methods defined, like `replaceAll`, `sort`, and 190 | `splitIterator`. 191 | 192 | ```java 193 | default void replaceAll(UnaryOperator operator) { 194 | Objects.requireNonNull(operator); 195 | final ListIterator li = this.listIterator(); 196 | while (li.hasNext()) { 197 | li.set(operator.apply(li.next())); 198 | } 199 | } 200 | ``` 201 | 202 | We can solve our API problem by defining a default method, as shown below. 203 | Default methods are usually defined using already existing methods -- 204 | `remainder` is defined using the `subtract`, `multiply`, and `divide` methods. 205 | 206 | ```java 207 | default int remainder(int number, int divisor) { 208 | return subtract(number, multiply(divisor, divide(number, divisor))); 209 | } 210 | ``` 211 | 212 | ## Multiple inheritance 213 | 214 | A class can extend a single class, but can implement multiple interfaces. Now 215 | that it is feasible to have method implementation in interfaces, Java has 216 | multiple inheritance of behavior. Java already had multiple inheritance at the 217 | type level, but now it also has multiple inheritance at the behavior level. 218 | There are three resolution rules that help decide which method will be picked: 219 | 220 | **Rule 1: Methods declared in classes win over methods defined in interfaces.** 221 | 222 | ```java 223 | interface A { 224 | default void doSth(){ 225 | System.out.println("inside A"); 226 | } 227 | } 228 | class App implements A{ 229 | 230 | @Override 231 | public void doSth() { 232 | System.out.println("inside App"); 233 | } 234 | 235 | public static void main(String[] args) { 236 | new App().doSth(); 237 | } 238 | } 239 | ``` 240 | 241 | This will print `inside App`, as methods declared in the implementing class have 242 | precedence over methods declared in interfaces. 243 | 244 | **Rule 2: Otherwise, the most specific interface is selected** 245 | 246 | ```java 247 | interface A { 248 | default void doSth() { 249 | System.out.println("inside A"); 250 | } 251 | } 252 | interface B {} 253 | interface C extends A { 254 | default void doSth() { 255 | System.out.println("inside C"); 256 | } 257 | } 258 | class App implements C, B, A { 259 | 260 | public static void main(String[] args) { 261 | new App().doSth(); 262 | } 263 | } 264 | ``` 265 | 266 | This will print `inside C`. 267 | 268 | **Rule 3: Otherwise, the class has to call the desired implementation 269 | unambiguously** 270 | 271 | ```java 272 | interface A { 273 | default void doSth() { 274 | System.out.println("inside A"); 275 | } 276 | } 277 | interface B { 278 | default void doSth() { 279 | System.out.println("inside B"); 280 | } 281 | } 282 | class App implements B, A { 283 | 284 | @Override 285 | public void doSth() { 286 | B.super.doSth(); 287 | } 288 | 289 | public static void main(String[] args) { 290 | new App().doSth(); 291 | } 292 | } 293 | ``` 294 | This will print `inside B`. 295 | 296 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/01-default-static-interface-methods)](https://github.com/igrigorik/ga-beacon) 297 | -------------------------------------------------------------------------------- /04-collectors.md: -------------------------------------------------------------------------------- 1 | Collectors [![TimeToRead](http://ttr.myapis.xyz/ttr.svg?pageUrl=https://github.com/shekhargulati/java8-the-missing-tutorial/blob/master/04-collectors.md)](http://ttr.myapis.xyz/) 2 | ------ 3 | 4 | On [day 2](http://shekhargulati.com/2015/07/26/day-2-lets-learn-about-streams/), 5 | you learned that the Stream API can help you work with collections in a 6 | declarative manner. We looked at `collect`, which is a terminal operation that 7 | collects the result set of a stream pipeline in a `List`. `collect` is a 8 | reduction operation that reduces a stream to a value. The value could be a 9 | Collection, Map, or a value object. You can use `collect` to achieve following: 10 | 11 | 1. **Reducing stream to a single value:** Result of the stream execution can be 12 | reduced to a single value. Single value could be a `Collection` or numeric value 13 | like int, double, etc or a custom value object. 14 | 15 | 2. **Group elements in a stream:** Group all the tasks in a stream by TaskType. 16 | This will result in a `Map>` with each entry containing a 17 | TaskType and its associated Tasks. You can use any other Collection instead of a 18 | List as well. If you don't need all the tasks associated with a TaskType, you 19 | can alternatively produce a `Map`. One example could be grouping 20 | tasks by type and obtaining the first created task. 21 | 22 | 3. **Partition elements in a stream:** You can partition a stream into two 23 | groups -- e.g. due and completed tasks. 24 | 25 | ## Collector in Action 26 | 27 | To feel the power of `Collector` let us look at the example where we have to 28 | group tasks by their type. In Java 8, we can achieve grouping by TaskType by 29 | writing code shown below. **Please refer to [day 2](http://shekhargulati.com/2015/07/26/day-2-lets-learn-about-streams/) 30 | blog where we talked about the example domain we will use in this series** 31 | 32 | ```java 33 | private static Map> groupTasksByType(List tasks) { 34 | return tasks.stream().collect(Collectors.groupingBy(task -> task.getType())); 35 | } 36 | ``` 37 | 38 | The code shown above uses `groupingBy` `Collector` defined in the `Collectors` 39 | utility class. It creates a Map with key as the `TaskType` and value as the list 40 | containing all the tasks which have same `TaskType`. To achieve the same in Java 41 | 7, you would have to write the following. 42 | 43 | ```java 44 | public static void main(String[] args) { 45 | List tasks = getTasks(); 46 | Map> allTasksByType = new HashMap<>(); 47 | for (Task task : tasks) { 48 | List existingTasksByType = allTasksByType.get(task.getType()); 49 | if (existingTasksByType == null) { 50 | List tasksByType = new ArrayList<>(); 51 | tasksByType.add(task); 52 | allTasksByType.put(task.getType(), tasksByType); 53 | } else { 54 | existingTasksByType.add(task); 55 | } 56 | } 57 | for (Map.Entry> entry : allTasksByType.entrySet()) { 58 | System.out.println(String.format("%s =>> %s", entry.getKey(), entry.getValue())); 59 | } 60 | } 61 | ``` 62 | 63 | ## Collectors: Common reduction operations 64 | 65 | The `Collectors` utility class provides a lot of static utility methods for 66 | creating collectors for most common use cases like accumulating elements into a 67 | Collection, grouping and partitioning elements, or summarizing elements 68 | according to various criteria. We will cover the most common `Collector`s in 69 | this blog. 70 | 71 | ## Reducing to a single value 72 | 73 | As discussed above, collectors can be used to collect stream output to a 74 | Collection or produce a single value. 75 | 76 | ### Collecting data into a List 77 | 78 | Let's write our first test case -- given a list of Tasks we want to collect all 79 | the titles into a List. 80 | 81 | ```java 82 | import static java.util.stream.Collectors.toList; 83 | 84 | public class Example2_ReduceValue { 85 | public List allTitles(List tasks) { 86 | return tasks.stream().map(Task::getTitle).collect(toList()); 87 | } 88 | } 89 | ``` 90 | 91 | The `toList` collector uses the List's `add` method to add elements into the 92 | resulting List. `toList` collector uses `ArrayList` as the List implementation. 93 | 94 | ### Collecting data into a Set 95 | 96 | If we want to make sure only unique titles are returned and we don't care about 97 | order then we can use `toSet` collector. 98 | 99 | ```java 100 | import static java.util.stream.Collectors.toSet; 101 | 102 | public Set uniqueTitles(List tasks) { 103 | return tasks.stream().map(Task::getTitle).collect(toSet()); 104 | } 105 | ``` 106 | 107 | The `toSet` method uses a `HashSet` as the Set implementation to store the 108 | result set. 109 | 110 | ### Collecting data into a Map 111 | 112 | You can convert a stream to a Map by using the `toMap` collector. The `toMap` 113 | collector takes two mapper functions to extract the key and values for the Map. 114 | In the code shown below, `Task::getTitle` is `Function` that takes a task and 115 | produces a key with only title. The **task -> task** is a lambda expression that 116 | just returns itself i.e. task in this case. 117 | 118 | ```java 119 | private static Map taskMap(List tasks) { 120 | return tasks.stream().collect(toMap(Task::getTitle, task -> task)); 121 | } 122 | ``` 123 | 124 | We can improve the code shown above by using the `identity` default method in 125 | the `Function` interface to make code cleaner and better convey developer 126 | intent, as shown below. 127 | 128 | ```java 129 | import static java.util.function.Function.identity; 130 | 131 | private static Map taskMap(List tasks) { 132 | return tasks.stream().collect(toMap(Task::getTitle, identity())); 133 | } 134 | ``` 135 | 136 | The code to create a Map from the stream will throw an exception when duplicate 137 | keys are present. You will get an error like the one shown below. 138 | 139 | ``` 140 | Exception in thread "main" java.lang.IllegalStateException: Duplicate key Task{title='Read Version Control with Git book', type=READING} 141 | at java.util.stream.Collectors.lambda$throwingMerger$105(Collectors.java:133) 142 | ``` 143 | 144 | You can handle duplicates by using another variant of the `toMap` function which 145 | allows us to specify a merge function. The merge function allows a client to 146 | specify how they want to resolve collisions between values associated with the 147 | same key. In the code shown below, we just used the newer value, but you can 148 | equally write an intelligent algorithm to resolve collisions. 149 | 150 | ```java 151 | private static Map taskMap_duplicates(List tasks) { 152 | return tasks.stream().collect(toMap(Task::getTitle, identity(), (t1, t2) -> t2)); 153 | } 154 | ``` 155 | 156 | You can use any other Map implementation by using the third variant of `toMap` 157 | method. This requires you to specify `Map` `Supplier` that will be used to store 158 | the result. 159 | 160 | ``` 161 | public Map collectToMap(List tasks) { 162 | return tasks.stream().collect(toMap(Task::getTitle, identity(), (t1, t2) -> t2, LinkedHashMap::new)); 163 | } 164 | ``` 165 | 166 | Similar to the `toMap` collector, there is also `toConcurrentMap` collector, 167 | which produces a `ConcurrentMap` instead of a `HashMap`. 168 | 169 | ### Using other collections 170 | 171 | The specific collectors like `toList` and `toSet` do not allow you to specify 172 | the underlying List or Set implementation. You can use the `toCollection` 173 | collector when you want to collect the result to other types of collections, as 174 | shown below. 175 | 176 | ``` 177 | private static LinkedHashSet collectToLinkedHaskSet(List tasks) { 178 | return tasks.stream().collect(toCollection(LinkedHashSet::new)); 179 | } 180 | ``` 181 | 182 | ### Finding Task with longest title 183 | 184 | ```java 185 | public Task taskWithLongestTitle(List tasks) { 186 | return tasks.stream().collect(collectingAndThen(maxBy((t1, t2) -> t1.getTitle().length() - t2.getTitle().length()), Optional::get)); 187 | } 188 | ``` 189 | 190 | ### Count total number of tags 191 | 192 | ```java 193 | public int totalTagCount(List tasks) { 194 | return tasks.stream().collect(summingInt(task -> task.getTags().size())); 195 | } 196 | ``` 197 | 198 | ### Generate summary of Task titles 199 | 200 | ```java 201 | public String titleSummary(List tasks) { 202 | return tasks.stream().map(Task::getTitle).collect(joining(";")); 203 | } 204 | ``` 205 | 206 | ## Grouping Collectors 207 | 208 | One of the most common use case of Collector is to group elements. Let's look at 209 | various examples to understand how we can perform grouping. 210 | 211 | ### Example 1: Grouping tasks by type 212 | 213 | Let's look at the example shown below, where we want to group all the tasks 214 | based on their `TaskType`. You can very easily perform this task by using the 215 | `groupingBy` Collector of the `Collectors` utility class. You can make it more 216 | succinct by using method references and static imports. 217 | 218 | ```java 219 | import static java.util.stream.Collectors.groupingBy; 220 | private static Map> groupTasksByType(List tasks) { 221 | return tasks.stream().collect(groupingBy(Task::getType)); 222 | } 223 | ``` 224 | 225 | It will produce the output shown below. 226 | 227 | ``` 228 | {CODING=[Task{title='Write a mobile application to store my tasks', type=CODING, createdOn=2015-07-03}], WRITING=[Task{title='Write a blog on Java 8 Streams', type=WRITING, createdOn=2015-07-04}], READING=[Task{title='Read Version Control with Git book', type=READING, createdOn=2015-07-01}, Task{title='Read Java 8 Lambdas book', type=READING, createdOn=2015-07-02}, Task{title='Read Domain Driven Design book', type=READING, createdOn=2015-07-05}]} 229 | ``` 230 | 231 | ### Example 2: Grouping by tags 232 | 233 | ```java 234 | private static Map> groupingByTag(List tasks) { 235 | return tasks.stream(). 236 | flatMap(task -> task.getTags().stream().map(tag -> new TaskTag(tag, task))). 237 | collect(groupingBy(TaskTag::getTag, mapping(TaskTag::getTask,toList()))); 238 | } 239 | 240 | private static class TaskTag { 241 | final String tag; 242 | final Task task; 243 | 244 | public TaskTag(String tag, Task task) { 245 | this.tag = tag; 246 | this.task = task; 247 | } 248 | 249 | public String getTag() { 250 | return tag; 251 | } 252 | 253 | public Task getTask() { 254 | return task; 255 | } 256 | } 257 | ``` 258 | 259 | ### Example 3: Group task by tag and count 260 | 261 | Combining classifiers and Collectors 262 | 263 | ```java 264 | private static Map tagsAndCount(List tasks) { 265 | return tasks.stream(). 266 | flatMap(task -> task.getTags().stream().map(tag -> new TaskTag(tag, task))). 267 | collect(groupingBy(TaskTag::getTag, counting())); 268 | } 269 | ``` 270 | 271 | ### Example 4: Grouping by TaskType and createdOn 272 | 273 | ```java 274 | private static Map>> groupTasksByTypeAndCreationDate(List tasks) { 275 | return tasks.stream().collect(groupingBy(Task::getType, groupingBy(Task::getCreatedOn))); 276 | } 277 | ``` 278 | 279 | ## Partitioning 280 | 281 | There are times when you want to partition a dataset into two datasets based on 282 | a predicate. For example, we can partition tasks into two groups by defining a 283 | partitioning function that partitions tasks into two groups -- one with due date 284 | before today, and one with the others. 285 | 286 | ```java 287 | private static Map> partitionOldAndFutureTasks(List tasks) { 288 | return tasks.stream().collect(partitioningBy(task -> task.getDueOn().isAfter(LocalDate.now()))); 289 | } 290 | ``` 291 | 292 | ## Generating statistics 293 | 294 | Another group of collectors that are very helpful are collectors that produce 295 | statistics. These work on the primitive datatypes like `int`, `double`, and 296 | `long`; and can be used to produce statistics like those shown below. 297 | 298 | ```java 299 | IntSummaryStatistics summaryStatistics = tasks.stream().map(Task::getTitle).collect(summarizingInt(String::length)); 300 | System.out.println(summaryStatistics.getAverage()); //32.4 301 | System.out.println(summaryStatistics.getCount()); //5 302 | System.out.println(summaryStatistics.getMax()); //44 303 | System.out.println(summaryStatistics.getMin()); //24 304 | System.out.println(summaryStatistics.getSum()); //162 305 | ``` 306 | 307 | There are other variants as well for other primitive types like 308 | `LongSummaryStatistics` and `DoubleSummaryStatistics` 309 | 310 | You can also combine one `IntSummaryStatistics` with another using the `combine` 311 | operation. 312 | 313 | ```java 314 | firstSummaryStatistics.combine(secondSummaryStatistics); 315 | System.out.println(firstSummaryStatistics) 316 | ``` 317 | 318 | ## Joining all titles 319 | 320 | ```java 321 | private static String allTitles(List tasks) { 322 | return tasks.stream().map(Task::getTitle).collect(joining(", ")); 323 | } 324 | ``` 325 | 326 | ## Writing a custom Collector 327 | 328 | ```java 329 | import com.google.common.collect.HashMultiset; 330 | import com.google.common.collect.Multiset; 331 | 332 | import java.util.Collections; 333 | import java.util.EnumSet; 334 | import java.util.Set; 335 | import java.util.function.BiConsumer; 336 | import java.util.function.BinaryOperator; 337 | import java.util.function.Function; 338 | import java.util.function.Supplier; 339 | import java.util.stream.Collector; 340 | 341 | public class MultisetCollector implements Collector, Multiset> { 342 | 343 | @Override 344 | public Supplier> supplier() { 345 | return HashMultiset::create; 346 | } 347 | 348 | @Override 349 | public BiConsumer, T> accumulator() { 350 | return (set, e) -> set.add(e, 1); 351 | } 352 | 353 | @Override 354 | public BinaryOperator> combiner() { 355 | return (set1, set2) -> { 356 | set1.addAll(set2); 357 | return set1; 358 | }; 359 | } 360 | 361 | @Override 362 | public Function, Multiset> finisher() { 363 | return Function.identity(); 364 | } 365 | 366 | @Override 367 | public Set characteristics() { 368 | return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH)); 369 | } 370 | } 371 | ``` 372 | 373 | ```java 374 | import com.google.common.collect.Multiset; 375 | 376 | import java.util.Arrays; 377 | import java.util.List; 378 | 379 | public class MultisetCollectorExample { 380 | 381 | public static void main(String[] args) { 382 | List names = Arrays.asList("shekhar", "rahul", "shekhar"); 383 | Multiset set = names.stream().collect(new MultisetCollector<>()); 384 | 385 | set.forEach(str -> System.out.println(str + ":" + set.count(str))); 386 | 387 | } 388 | } 389 | ``` 390 | 391 | ## Word Count in Java 8 392 | 393 | We will end this section by writing the famous word count example in Java 8 394 | using Streams and Collectors. 395 | 396 | ```java 397 | public static void wordCount(Path path) throws IOException { 398 | Map wordCount = Files.lines(path) 399 | .parallel() 400 | .flatMap(line -> Arrays.stream(line.trim().split("\\s"))) 401 | .map(word -> word.replaceAll("[^a-zA-Z]", "").toLowerCase().trim()) 402 | .filter(word -> word.length() > 0) 403 | .map(word -> new SimpleEntry<>(word, 1)) 404 | .collect(groupingBy(SimpleEntry::getKey, counting())); 405 | wordCount.forEach((k, v) -> System.out.println(String.format("%s ==>> %d", k, v))); 406 | } 407 | ``` 408 | 409 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/04-collectors)](https://github.com/igrigorik/ga-beacon) 410 | -------------------------------------------------------------------------------- /05-optionals.md: -------------------------------------------------------------------------------- 1 | Optionals [![TimeToRead](http://ttr.myapis.xyz/ttr.svg?pageUrl=https://github.com/shekhargulati/java8-the-missing-tutorial/blob/master/05-optionals.md)](http://ttr.myapis.xyz/) 2 | ---- 3 | 4 | Every Java developer, whether beginner, novice, or seasoned, has in their lifetime experienced `NullPointerException`. We all have wasted or spent many hours trying to fix bugs caused by `NullPointerException`. According to `NullPointerException`'s JavaDoc, ***NullPointerException is thrown when an application attempts to use null in a case where an object is required.***. This means if we invoke a method or try to access a property on ***null*** reference, then our code will explode and `NullPointerException` is thrown. In this chapter, you will learn how to write null-free code using Java 8's `Optional`. 5 | 6 | > On a lighter note, if you look at the JavaDoc of NullPointerException you will find that author of this exception is ***unascribed***. If the author is unknown, unascribed is used (nobody wants to take ownership of NullPointerException ;)). 7 | 8 | ## What are null references? 9 | 10 | In 2009, at QCon conference, ***[Sir Tony Hoare](https://en.wikipedia.org/wiki/Tony_Hoare)*** stated that he invented the `null` reference type while designing the ***ALGOL W*** programming language. `null` was designed to signify absence of a value. He called *null references* as a *billion-dollar mistake*. You can watch the full video of his presentation on Infoq http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare. 11 | 12 | Most of the programming languages like C, C++, C#, Java, Scala, etc. have a nullable type as part of their type system, which allows the value to be set to a special value, **Null**, instead of other possible data type values. 13 | 14 | ## Why null references are a bad thing 15 | 16 | Let's look at the example Task management domain classes shown below. Our domain model is very simple with only two classes -- Task and User. A task can be assigned to a user. 17 | 18 | > Code for this section is inside [ch05 package](https://github.com/shekhargulati/java8-the-missing-tutorial/tree/master/code/src/main/java/com/shekhargulati/java8_tutorial/ch05). 19 | 20 | ```java 21 | public class Task { 22 | private final String id; 23 | private final String title; 24 | private final TaskType type; 25 | private User assignedTo; 26 | 27 | public Task(String id, String title, TaskType type) { 28 | this.id = id; 29 | this.title = title; 30 | this.type = type; 31 | } 32 | 33 | public Task(String title, TaskType type) { 34 | this.id = UUID.randomUUID().toString(); 35 | this.title = title; 36 | this.type = type; 37 | } 38 | 39 | public String getTitle() { 40 | return title; 41 | } 42 | 43 | public TaskType getType() { 44 | return type; 45 | } 46 | 47 | public User getAssignedTo() { 48 | return assignedTo; 49 | } 50 | 51 | public void setAssignedTo(User assignedTo) { 52 | this.assignedTo = assignedTo; 53 | } 54 | } 55 | 56 | public class User { 57 | 58 | private final String username; 59 | private String address; 60 | 61 | public User(String username) { 62 | this.username = username; 63 | } 64 | 65 | public String getUsername() { 66 | return username; 67 | } 68 | 69 | public String getAddress() { 70 | return address; 71 | } 72 | 73 | public void setAddress(String address) { 74 | this.address = address; 75 | } 76 | } 77 | ``` 78 | 79 | Given the above domain model, if we have to find the User who is assigned a task with id `taskId` then we would write code as shown below. 80 | 81 | ```java 82 | public String taskAssignedTo(String taskId) { 83 | return taskRepository.find(taskId).getAssignedTo().getUsername(); 84 | } 85 | ``` 86 | 87 | The biggest problem with the code shown above is that absence of the value is not visible in the API, i.e. if the `task` is not assigned to any user, then the code will throw `NullPointerException` when `getAssignedTo` is called. The `taskRepository.find(taskId)` and `taskRepository.find(taskId).getAssignedTo()` could return `null`. This forces clients of the API to program defensively, and check for null frequently, as shown below. 88 | 89 | ```java 90 | public String taskAssignedTo(String taskId) throws TaskNotFoundException { 91 | Task task = taskRepository.find(taskId); 92 | if (task != null) { 93 | User assignedTo = task.getAssignedTo(); 94 | if (assignedTo != null) 95 | return assignedTo.getUsername(); 96 | return "NotAssigned"; 97 | } 98 | throw new TaskNotFoundException(String.format("No task exist with id '%s'", taskId)); 99 | } 100 | ``` 101 | 102 | The code shown above misses developer intent and bloats client code with `if-null` checks. The developer somehow wanted to use optional data type but he was forced to write `if-null` checks. I am sure you would have written this kind of code in your day to day programming. 103 | 104 | ## Null Object pattern 105 | 106 | A common solution to working with `null` references is to use [Null Object pattern](https://en.wikipedia.org/wiki/Null_Object_pattern). The idea behind this pattern is very simple -- instead of returning null, you should return a null object that implements your interface or class. So, you can create a `NullUser` as shown below. 107 | 108 | ```java 109 | public class NullUser extends User { 110 | 111 | public NullUser(String username) { 112 | super("NotAssigned"); 113 | } 114 | } 115 | ``` 116 | 117 | So now we could return a `NullUser` when no user is assigned a task. We can change the `getAssignedTo` method to return `NullUser` when no user is assigned a task. 118 | 119 | ```java 120 | public User getAssignedTo() { 121 | return assignedTo == null ? NullUser.getInstance() : assignedTo; 122 | } 123 | ``` 124 | 125 | Now client code can be simplified to not use a null check for `User`, as shown below. In this example, it does not make sense to use Null Object pattern for `Task` because non-existence of task in the repository is an exception situation. Also, by adding `TaskNotFoundException` in the `throws` clause, we have made it explicit for the client that this code can throw an exception. 126 | 127 | ```java 128 | public String taskAssignedTo(String taskId) throws TaskNotFoundException { 129 | Task task = taskRepository.find(taskId); 130 | if (task != null) { 131 | return task.getAssignedTo().getUsername(); 132 | } 133 | throw new TaskNotFoundException(String.format("No task exist with id '%s'", taskId)); 134 | } 135 | ``` 136 | 137 | ## Java 8 -- Introduction of Optional data type 138 | 139 | Java 8 introduced a new data type, `java.util.Optional`, which encapsulates an empty value. It makes the intent of the API clear. If a function returns a value of type `Optional`, then it tells the clients that a value might not be present. Use of the `Optional` data type makes it explicit to the API client when it should expect an optional value. The purpose of using the `Optional` type is to help API designers make it visible to their clients, by looking at the method signature, whether they should expect an optional value or not. 140 | 141 | Let's update our domain model to reflect optional values. 142 | 143 | ```java 144 | import java.util.Optional; 145 | 146 | public class Task { 147 | private final String title; 148 | private final User assignedTo; 149 | private final String id; 150 | 151 | public Task(String id, String title) { 152 | this.id = id; 153 | this.title = title; 154 | } 155 | 156 | public Task(String id, String title, User assignedTo) { 157 | this.id = id; 158 | this.title = title; 159 | this.assignedTo = assignedTo; 160 | } 161 | 162 | public String getId() { 163 | return id; 164 | } 165 | 166 | public String getTitle() { 167 | return title; 168 | } 169 | 170 | public Optional getAssignedTo() { 171 | return assignedTo; 172 | } 173 | } 174 | 175 | import java.util.Optional; 176 | 177 | public class User { 178 | 179 | private final String username; 180 | private final String address; 181 | 182 | public User(String username) { 183 | this.username = username; 184 | } 185 | 186 | public User(String username, String address) { 187 | this.username = username; 188 | this.address = address; 189 | } 190 | 191 | public String getUsername() { 192 | return username; 193 | } 194 | 195 | public Optional getAddress() { 196 | return address; 197 | } 198 | } 199 | ``` 200 | 201 | Since `Task.assignedTo` and `User.username` are nullable fields, each getter should return `Optional` and `Optional`. Now whoever tries to work with `assignedTo` User would know that it might not be present and they can handle it in a declarative way. We will talk about `Optional.empty` and `Optional.of` methods in the next section. 202 | 203 | ## Working with creational methods in the java.util.Optional API 204 | 205 | In the domain model shown above, we used a couple of creational methods of the Optional class, but didn't discuss them. Three creational methods which are part of the `Optional` API follow below. 206 | 207 | * `Optional.empty`: This is used to create an Optional when a value is not present, like we did above (`this.assignedTo = Optional.empty();`) in the constructor. 208 | 209 | * `Optional.of(T value)`: This is used to create an Optional from a non-null value. It throws `NullPointerException` when `value` is null. We used it in the code shown above (`this.address = Optional.of(address);`). 210 | 211 | * `Optional.ofNullable(T value)`: This static factory method which works for both null and non-null values. For null values it will create an empty Optional and for non-null values it will create an Optional using the value. 212 | 213 | Below is a simple example of how you can write an API using Optional. 214 | 215 | ```java 216 | public class TaskRepository { 217 | 218 | private static final Map TASK_STORE = new ConcurrentHashMap<>(); 219 | 220 | public Optional find(String taskId) { 221 | return Optional.ofNullable(TASK_STORE.get(taskId)); 222 | } 223 | 224 | public void add(Task task) { 225 | TASK_STORE.put(task.getId(), task); 226 | } 227 | } 228 | ``` 229 | 230 | ## Using Optional values 231 | 232 | Optional can be thought of as a Stream with one element. It has methods similar to Stream API like `map`, `filter`, and `flatMap`, which we can use to work with values contained in the `Optional`. 233 | 234 | ### Getting title for a Task 235 | 236 | To read the value of title for a Task, we would write code as shown below. The `map` function was used to transform from `Optional` to `Optional`. The `orElseThrow` method is used to throw a custom business exception when no Task is found. 237 | 238 | ```java 239 | public String taskTitle(String taskId) { 240 | return taskRepository. 241 | find(taskId). 242 | map(Task::getTitle). 243 | orElseThrow(() -> new TaskNotFoundException(String.format("No task exist for id '%s'",taskId))); 244 | } 245 | ``` 246 | 247 | There are three variants of `orElse*` method: 248 | 249 | 1. `orElse(T t)`: This is used to return the value if it exists, or returns the value `t` passed as parameter, like `Optional.ofNullable(null).orElse("NoValue")`. This will return `"NoValue"` as no value exists. 250 | 251 | 2. `orElseGet`: This will return the value if it is present, otherwise invokes the `Supplier`'s `get` method to produce a new value. For example, `Optional.ofNullable(null).orElseGet(() -> UUID.randomUUID().toString()` could be used to lazily produce a value only when none is present. 252 | 253 | 3. `orElseThrow`: This allow clients to throw their own custom exception when a value is not present. 254 | 255 | The find method shown above returns an `Optional` that the client can use to get the value. Suppose we want to get the task's title from the `Optional` -- we can do that by using the `map` function, as shown below. 256 | 257 | ### Getting username of the assigned user 258 | 259 | To get the username of the user who is assigned a task, we can use the `flatMap` method, as shown below. 260 | 261 | ```java 262 | public String taskAssignedTo(String taskId) { 263 | return taskRepository. 264 | find(taskId). 265 | flatMap(task -> task.getAssignedTo().map(user -> user.getUsername())). 266 | orElse("NotAssigned"); 267 | } 268 | ``` 269 | 270 | ### Filtering with Optional 271 | 272 | The third Stream API like operation supported by `Optional` is `filter`, which allows you to filter an Optional based on some property, as shown in the example below. 273 | 274 | ```java 275 | public boolean isTaskDueToday(Optional task) { 276 | return task.flatMap(Task::getDueOn).filter(d -> d.isEqual(LocalDate.now())).isPresent(); 277 | } 278 | ``` 279 | 280 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/05-optionals)](https://github.com/igrigorik/ga-beacon) 281 | -------------------------------------------------------------------------------- /06-map.md: -------------------------------------------------------------------------------- 1 | Map Improvements 2 | --------- 3 | 4 | Map is one the most important data structure. In Java 8, a lot of goodies has 5 | been added to the Map API that will make it easy to work with them. We will look 6 | at all the enhancements made to them one by one. Every feature is shown along 7 | with its JUnit test case. 8 | 9 | ## Create Map from List 10 | 11 | Most of the times we want to create a map from existing data. Let's suppose we 12 | have a list of tasks, every task has an id and other associated data like title, 13 | description, etc. 14 | 15 | ```java 16 | import static java.util.function.Function.identity; 17 | import static java.util.stream.Collectors.toMap; 18 | 19 | @Test 20 | public void shouldCreateMapFromTaskList() throws Exception { 21 | Task t1 = new Task("Write blog on Java 8 Map improvements", TaskType.BLOGGING); 22 | Task t2 = new Task("Write factorial program in Java 8", TaskType.CODING); 23 | List tasks = Arrays.asList(t1, t2); 24 | 25 | Map taskIdToTaskMap = tasks.stream().collect(toMap(Task::getId, identity())); 26 | 27 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t1))); 28 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t2))); 29 | } 30 | ``` 31 | 32 | ## Using a different Map implementation 33 | 34 | The default implementation used by `Collectors.toMap` is `HashMap`. You can also 35 | specify your own Map implementation by providing a supplier. 36 | 37 | ```java 38 | @Test 39 | public void shouldCreateLinkedMapFromTaskList() throws Exception { 40 | Task t1 = new Task("Write blog on Java 8 Map improvements", TaskType.BLOGGING); 41 | Task t2 = new Task("Write factorial program in Java 8", TaskType.CODING); 42 | List tasks = Arrays.asList(t1, t2); 43 | 44 | Map taskIdToTaskMap = tasks.stream().collect(toMap(Task::getId, identity(), (k1, k2) -> k1, LinkedHashMap::new)); 45 | 46 | assertThat(taskIdToTaskMap, instanceOf(LinkedHashMap.class)); 47 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t1))); 48 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t2))); 49 | } 50 | ``` 51 | 52 | ## Handling duplicates 53 | 54 | One thing that we glossed over in the last example was what should happen if 55 | there are duplicates. To handle duplicates there is a argument 56 | 57 | ```java 58 | @Test 59 | public void shouldHandleTaskListWithDuplicates() throws Exception { 60 | Task t1 = new Task("1", "Write blog on Java 8 Map improvements", TaskType.BLOGGING); 61 | Task t2 = new Task("1", "Write factorial program in Java 8", TaskType.CODING); 62 | List tasks = Arrays.asList(t1, t2); 63 | 64 | Map taskIdToTaskMap = tasks.stream().collect(toMap(Task::getId, identity())); 65 | 66 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t1))); 67 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t2))); 68 | } 69 | ``` 70 | 71 | This test will fail 72 | 73 | ``` 74 | java.lang.IllegalStateException: Duplicate key Task{title='Write blog on Java 8 Map improvements', type=BLOGGING} 75 | ``` 76 | 77 | You can handle the error by specifying your merge function. 78 | 79 | ```java 80 | @Test 81 | public void shouldHandleTaskListWithDuplicates() throws Exception { 82 | Task t1 = new Task("1", "Write blog on Java 8 Map improvements", TaskType.BLOGGING); 83 | Task t2 = new Task("1", "Write factorial program in Java 8", TaskType.CODING); 84 | List tasks = Arrays.asList(t1, t2); 85 | Map taskIdToTaskMap = tasks.stream().collect(toMap(Task::getId, identity(), (k1, k2) -> k2)); 86 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t2))); 87 | } 88 | ``` 89 | 90 | ## Create Map from tuples 91 | 92 | ```java 93 | public static Map createMap(SimpleEntry... entries) { 94 | return Stream.of(entries).collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue)); 95 | } 96 | ``` 97 | 98 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/06-map)](https://github.com/igrigorik/ga-beacon) 99 | -------------------------------------------------------------------------------- /07-building-functional-programs.md: -------------------------------------------------------------------------------- 1 | Building Functional Programs 2 | -------- 3 | 4 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/07-building-functional-programs)](https://github.com/igrigorik/ga-beacon) 5 | -------------------------------------------------------------------------------- /08-date-time-api.md: -------------------------------------------------------------------------------- 1 | Date Time API [![TimeToRead](http://ttr.myapis.xyz/ttr.svg?pageUrl=https://github.com/shekhargulati/java8-the-missing-tutorial/blob/master/08-date-time-api.md)](http://ttr.myapis.xyz/) 2 | ------- 3 | 4 | So far in this [book](https://github.com/shekhargulati/java8-the-missing-tutorial) 5 | we have focussed on [functional](./02-lambdas.md) [aspects](03-streams.md) of 6 | Java 8 and looked at how to design better API's using [Optional](05-optionals.md) 7 | and [default and static methods in Interfaces](./01-default-static-interface-methods.md). 8 | In this chapter, we will learn about another new API that will change the way we 9 | work with dates -- Date Time API. Almost all Java developers will agree that 10 | date and time support prior to Java 8 is far from ideal and most of the time we 11 | had to use third party libraries like [Joda-Time](http://www.joda.org/joda-time/) 12 | in our applications. The new Date Time API is heavily influenced by Joda-Time API 13 | and if you have used it then you will feel home. 14 | 15 | ## What's wrong with existing Date API? 16 | 17 | Before we learn about new Date Time API let's understand why existing Date API 18 | sucks. Look at the code shown below and try to answer what it will print. 19 | 20 | ```java 21 | import java.util.Date; 22 | 23 | public class DateSucks { 24 | 25 | public static void main(String[] args) { 26 | Date date = new Date(12, 12, 12); 27 | System.out.println(date); 28 | } 29 | } 30 | ``` 31 | 32 | Can you answer what above code prints? Most Java developers will expect the 33 | program to print `0012-12-12` but the above code prints `Sun Jan 12 00:00:00 IST 34 | 1913`. My first reaction when I learnt that program prints `Sun Jan 12 00:00:00 35 | IST 1913` was WTF??? 36 | 37 | The code shown above has following issues: 38 | 39 | 1. What each 12 means? Is it month, year, date or date, month, year or any other 40 | combination. 41 | 42 | 2. Date API month index starts at 0. So, December is actually 11. 43 | 44 | 3. Date API rolls over i.e. 12 will become January. 45 | 46 | 4. Year starts with 1900. And because month also roll over so year becomes `1900 47 | + 12 + 1 == 1913`. Go figure!! 48 | 49 | 5. Who asked for time? I just asked for date but program prints time as well. 50 | 51 | 6. Why is there time zone? Who asked for it? The time zone is JVM's default time 52 | zone, IST, Indian Standard Time in this example. 53 | 54 | > Date API is close to 20 years old introduced with JDK 1.0. One of the original 55 | > authors of Date API is none other than James Gosling himself -- Father of Java 56 | > Programming Language. 57 | 58 | There are many other design issues with Date API like mutability, separate class 59 | hierarchy for SQL, etc. In JDK1.1 effort was made to provide a better API i.e. 60 | `Calendar` but it was also plagued with similar issues of mutability and index 61 | starting at 0. 62 | 63 | ## Java 8 Date Time API 64 | 65 | Java 8 Date Time API was developed as part of JSR-310 and reside insides 66 | `java.time` package. The API applies **domain-driven design** principles with 67 | domain classes like LocalDate, LocalTime applicable to solve problems related to 68 | their specific domains of date and time. This makes API intent clear and easy to 69 | understand. The other design principle applied is the **immutability**. All the 70 | core classes in the `java.time` are immutable hence avoiding thread-safety 71 | issues. 72 | 73 | ## Getting Started with Java 8 Date Time API 74 | 75 | The three classes that you will encounter most in the new API are `LocalDate`, 76 | `LocalTime`, and `LocalDateTime`. Their description is like their name suggests: 77 | 78 | * **LocalDate**: It represents a date with no time or timezone. 79 | 80 | * **LocalTime**: It represents time with no date or timezone 81 | 82 | * **LocalDateTime**: It is the combination of LocalDate and LocalTime i.e. date 83 | with time without time zone. 84 | 85 | > We will use JUnit to drive our examples. We will first write a JUnit case that 86 | > will explain what we are trying to do and then we will write code to make the 87 | > test pass. Examples will be based on great Indian president -- 88 | > [A.P.J Abdul Kalam](https://en.wikipedia.org/wiki/A._P._J._Abdul_Kalam). 89 | 90 | ### Kalam was born on 15th October 1931 91 | 92 | ```java 93 | import org.junit.Test; 94 | import java.time.LocalDate; 95 | import static org.hamcrest.CoreMatchers.equalTo; 96 | import static org.junit.Assert.assertThat; 97 | 98 | public class DateTimeExamplesTest { 99 | private AbdulKalam kalam = new AbdulKalam(); 100 | @Test 101 | public void kalamWasBornOn15October1931() throws Exception { 102 | LocalDate dateOfBirth = kalam.dateOfBirth(); 103 | assertThat(dateOfBirth.toString(), equalTo("1931-10-15")); 104 | } 105 | } 106 | ``` 107 | 108 | `LocalDate` has a static factory method `of` that takes year, month, and date 109 | and gives you a `LocalDate`. To make this test pass, we will write `dateOfBirth` 110 | method in `AbdulKalam` class using `of` method as shown below. 111 | 112 | ```java 113 | import java.time.LocalDate; 114 | import java.time.Month; 115 | 116 | public class AbdulKalam { 117 | public LocalDate dateOfBirth() { 118 | return LocalDate.of(1931, Month.OCTOBER, 15); 119 | } 120 | } 121 | ``` 122 | 123 | There is an overloaded `of` method that takes month as integer instead of 124 | `Month` enum. I recommend using `Month` enum as it is more readable and clear. 125 | There are two other static factory methods to create `LocalDate` instances -- 126 | `ofYearDay` and `ofEpochDay`. 127 | 128 | The `ofYearDay` creates LocalDate instance from the year and day of year for 129 | example March 31st 2015 is the 90th day in 2015 so we can create LocalDate using 130 | `LocalDate.ofYearDay(2015, 90)`. 131 | 132 | ```java 133 | LocalDate january_21st = LocalDate.ofYearDay(2015, 21); 134 | System.out.println(january_21st); // 2015-01-21 135 | LocalDate march_31st = LocalDate.ofYearDay(2015, 90); 136 | System.out.println(march_31st); // 2015-03-31 137 | ``` 138 | 139 | The `ofEpochDay` creates LocalDate instance using the epoch day count. The 140 | starting value of epoch is `1970-01-01`. So, `LocalDate.ofEpochDay(1)` will give 141 | `1970-01-02`. 142 | 143 | LocalDate instance provide many accessor methods to access different fields like 144 | year, month, dayOfWeek, etc. 145 | 146 | ```java 147 | @Test 148 | public void kalamWasBornOn15October1931() throws Exception { 149 | LocalDate dateOfBirth = kalam.dateOfBirth(); 150 | assertThat(dateOfBirth.getMonth(), is(equalTo(Month.OCTOBER))); 151 | assertThat(dateOfBirth.getYear(), is(equalTo(1931))); 152 | assertThat(dateOfBirth.getDayOfMonth(), is(equalTo(15))); 153 | assertThat(dateOfBirth.getDayOfYear(), is(equalTo(288))); 154 | } 155 | ``` 156 | 157 | You can create current date from the system clock using `now` static factory 158 | method. 159 | 160 | ```java 161 | LocalDate.now() 162 | ``` 163 | 164 | ### Kalam was born at 01:15am 165 | 166 | ```java 167 | @Test 168 | public void kalamWasBornAt0115() throws Exception { 169 | LocalTime timeOfBirth = kalam.timeOfBirth(); 170 | assertThat(timeOfBirth.toString(), is(equalTo("01:15"))); 171 | } 172 | ``` 173 | 174 | `LocalTime` class is used to work with time. Just like `LocalDate`, it also 175 | provides static factory methods for creating its instances. We will use the `of` 176 | static factory method giving it hour and minute and it will return LocalTime as 177 | shown below. 178 | 179 | ```java 180 | public LocalTime timeOfBirth() { 181 | return LocalTime.of(1, 15); 182 | } 183 | ``` 184 | 185 | There are other overloaded variants of `of` method that can take second and 186 | nanosecond. 187 | 188 | > LocalTime is represented to nanosecond precision. 189 | 190 | You can print the current time of the system clock using `now` method as shown 191 | below. 192 | 193 | ```java 194 | LocalTime.now() 195 | ``` 196 | 197 | You can also create instances of `LocalTime` from seconds of day or nanosecond 198 | of day using `ofSecondOfDay` and `ofNanoOfDay` static factory methods. 199 | 200 | Similar to `LocalDate` `LocalTime` also provide accessor for its field as shown 201 | below. 202 | 203 | ```java 204 | @Test 205 | public void kalamWasBornAt0115() throws Exception { 206 | LocalTime timeOfBirth = kalam.timeOfBirth(); 207 | assertThat(timeOfBirth.getHour(), is(equalTo(1))); 208 | assertThat(timeOfBirth.getMinute(), is(equalTo(15))); 209 | assertThat(timeOfBirth.getSecond(), is(equalTo(0))); 210 | } 211 | ``` 212 | 213 | ### Kalam was born on 15 October at 01:15 am 214 | 215 | When you want to represent both date and time together then you can use 216 | `LocalDateTime`. LocalDateTime also provides many static factory methods to 217 | create its instances. We can use `of` factory method that takes a `LocalDate` 218 | and `LocalTime` and gives `LocalDateTime` instance as shown below. 219 | 220 | ```java 221 | public LocalDateTime dateOfBirthAndTime() { 222 | return LocalDateTime.of(dateOfBirth(), timeOfBirth()); 223 | } 224 | ``` 225 | 226 | There are many overloaded variants of `of` method which as arguments take year, 227 | month, day, hour, min, secondOfDay, nanosecondOfDay. 228 | 229 | ![LocalDateTime Of Methods](https://whyjava.files.wordpress.com/2015/10/localdatetime_of.png) 230 | 231 | To create current date and time using system clock you can use `now` factory 232 | method. 233 | 234 | ```java 235 | LocalDateTime.now() 236 | ``` 237 | 238 | ## Manipulating dates 239 | 240 | Now that we know how to create instances of `LocalDate`, `LocalTime`, and 241 | `LocalDateTime` let's learn how we can manipulate them. 242 | 243 | > LocalDate, LocalTime, and LocalDateTime are immutable so each time you perform 244 | > a manipulation operation you get a new instance. 245 | 246 | ### Kalam 50th birthday was on Thursday 247 | 248 | ```java 249 | @Test 250 | public void kalam50thBirthDayWasOnThursday() throws Exception { 251 | DayOfWeek dayOfWeek = kalam.dayOfBirthAtAge(50); 252 | assertThat(dayOfWeek, is(equalTo(DayOfWeek.THURSDAY))); 253 | } 254 | ``` 255 | 256 | We can use `dateOfBirth` method that we wrote earlier with `plusYears` on 257 | `LocalDate` instance to achieve this as shown below. 258 | 259 | ```java 260 | public DayOfWeek dayOfBirthAtAge(final int age) { 261 | return dateOfBirth().plusYears(age).getDayOfWeek(); 262 | } 263 | ``` 264 | 265 | There are similar `plus*` variants for adding days, months, weeks to the value. 266 | 267 | Similar to `plus` methods there are `minus` methods that allow you minus year, 268 | days, months from a `LocalDate` instance. 269 | 270 | ```java 271 | LocalDate today = LocalDate.now(); 272 | LocalDate yesterday = today.minusDays(1); 273 | ``` 274 | 275 | > Just like LocalDate LocalTime and LocalDateTime also provide similar `plus*` 276 | > and `minus*` methods. 277 | 278 | ### List all Kalam's birthdate DayOfWeek 279 | 280 | For this use-case, we will create an infinite stream of `LocalDate` starting 281 | from the Kalam's date of birth using the `Stream.iterate` method. 282 | `Stream.iterate` method takes a starting value and a function that allows you to 283 | work on the initial seed value and return another value. We just incremented the 284 | year by 1 and return next year birthdate. Then we transformed `LocalDate` to 285 | `DayOfWeek` to get the desired output value. Finally, we limited our result set 286 | to the provided limit and collected Stream result into a List. 287 | 288 | ```java 289 | public List allBirthDateDayOfWeeks(int limit) { 290 | return Stream.iterate(dateOfBirth(), db -> db.plusYears(1)) 291 | .map(LocalDate::getDayOfWeek) 292 | .limit(limit) 293 | .collect(toList()); 294 | } 295 | ``` 296 | 297 | ## Duration and Period 298 | 299 | `Duration` and `Period` classes represents quantity or amount of time. 300 | 301 | **Duration** represents quantity or amount of time in seconds, nano-seconds, or 302 | days like 10 seconds. 303 | 304 | **Period** represents amount or quantity of time in years, months, and days. 305 | 306 | ### Calculate number of days kalam lived 307 | 308 | ```java 309 | @Test 310 | public void kalamLived30601Days() throws Exception { 311 | long daysLived = kalam.numberOfDaysLived(); 312 | assertThat(daysLived, is(equalTo(30601L))); 313 | } 314 | ``` 315 | 316 | To calculate number of days kalam lived we can use `Duration` class. `Duration` 317 | has a factory method that takes two `LocalTime`, or `LocalDateTime` or 318 | `Instant` and gives a duration. The duration can then be converted to days, 319 | hours, seconds, etc. 320 | 321 | ```java 322 | public Duration kalamLifeDuration() { 323 | LocalDateTime deathDateAndTime = LocalDateTime.of(LocalDate.of(2015, Month.JULY, 27), LocalTime.of(19, 0)); 324 | return Duration.between(dateOfBirthAndTime(), deathDateAndTime); 325 | } 326 | 327 | public long numberOfDaysLived() { 328 | return kalamLifeDuration().toDays(); 329 | } 330 | ``` 331 | 332 | ### Kalam lived 83 years 9 months and 12 days 333 | 334 | ```java 335 | @Test 336 | public void kalamLifePeriod() throws Exception { 337 | Period kalamLifePeriod = kalam.kalamLifePeriod(); 338 | assertThat(kalamLifePeriod.getYears(), is(equalTo(83))); 339 | assertThat(kalamLifePeriod.getMonths(), is(equalTo(9))); 340 | assertThat(kalamLifePeriod.getDays(), is(equalTo(12))); 341 | } 342 | ``` 343 | 344 | We can use `Period` class to calculate number of years, months, and days kalam 345 | lived as shown below. Period's `between` method works with `LocalDate` only. 346 | 347 | ```java 348 | public Period kalamLifePeriod() { 349 | LocalDate deathDate = LocalDate.of(2015, Month.JULY, 27); 350 | return Period.between(dateOfBirth(), deathDate); 351 | } 352 | ``` 353 | 354 | ## Printing and Parsing dates 355 | 356 | In our day-to-day applications a lot of times we have to parse a text format to 357 | a date or time or we have to print a date or time in a specific format. Printing 358 | and parsing are very common use cases when working with date or time. Java 8 359 | provides a class `DateTimeFormatter` which is the main class for formatting and 360 | printing. All the classes and interfaces relevant to them resides inside the 361 | `java.time.format` package. 362 | 363 | ### Print Kalam birthdate in Indian date format 364 | 365 | In India, `dd-MM-YYYY` is the predominant date format that is used in all the 366 | government documents like passport application form. You can read more about 367 | Date and time notation in India on the 368 | [wikipedia](https://en.wikipedia.org/wiki/Date_and_time_notation_in_India). 369 | 370 | ```java 371 | @Test 372 | public void kalamDateOfBirthFormattedInIndianDateFormat() throws Exception { 373 | final String indianDateFormat = "dd-MM-YYYY"; 374 | String dateOfBirth = kalam.formatDateOfBirth(indianDateFormat); 375 | assertThat(dateOfBirth, is(equalTo("15-10-1931"))); 376 | } 377 | ``` 378 | 379 | The `formatDateofBirth` method uses `DateTimeFormatter` `ofPattern` method to 380 | create a new formatter using the specified pattern. All the main main date-time 381 | classes provide two methods - one for formatting, `format(DateTimeFormatter 382 | formatter)`, and one for parsing, `parse(CharSequence text, DateTimeFormatter 383 | formatter)`. 384 | 385 | ```java 386 | public String formatDateOfBirth(final String pattern) { 387 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); 388 | return dateOfBirth().format(formatter); 389 | } 390 | ``` 391 | 392 | For the common use cases, `DateTimeFormatter` class provides formatters as 393 | static constants. There are predefined constants for `BASIC_ISO_DATE` i.e 394 | **20111203** or `ISO_DATE` i.e. **2011-12-03**, etc that developers can easily 395 | use in their code. In the code shown below, you can see how to use these 396 | predefined formats. 397 | 398 | ```java 399 | @Test 400 | public void kalamDateOfBirthInDifferentDateFormats() throws Exception { 401 | LocalDate kalamDateOfBirth = LocalDate.of(1931, Month.OCTOBER, 15); 402 | assertThat(kalamDateOfBirth.format(DateTimeFormatter.BASIC_ISO_DATE), is(equalTo("19311015"))); 403 | assertThat(kalamDateOfBirth.format(DateTimeFormatter.ISO_LOCAL_DATE), is(equalTo("1931-10-15"))); 404 | assertThat(kalamDateOfBirth.format(DateTimeFormatter.ISO_ORDINAL_DATE), is(equalTo("1931-288"))); 405 | } 406 | ``` 407 | 408 | ### Parsing text to LocalDateTime 409 | 410 | Let's suppose we have to parse `15 Oct 1931 01:15 AM` to a LocalDateTime 411 | instance as shown in code below. 412 | 413 | ```java 414 | @Test 415 | public void shouldParseKalamDateOfBirthAndTimeToLocalDateTime() throws Exception { 416 | final String input = "15 Oct 1931 01:15 AM"; 417 | LocalDateTime dateOfBirthAndTime = kalam.parseDateOfBirthAndTime(input); 418 | assertThat(dateOfBirthAndTime.toString(), is(equalTo("1931-10-15T01:15"))); 419 | } 420 | ``` 421 | 422 | We will again use `DateTimeFormatter` `ofPattern` method to create a new 423 | `DateTimeFormatter` and then use the `parse` method of `LocalDateTime` to create 424 | a new instance of `LocalDateTime` as shown below. 425 | 426 | ```java 427 | public LocalDateTime parseDateOfBirthAndTime(String input) { 428 | return LocalDateTime.parse(input, DateTimeFormatter.ofPattern("dd MMM yyyy hh:mm a")); 429 | } 430 | ``` 431 | 432 | ## Advance date time manipulation with TemporalAdjusters 433 | 434 | In `Manipulating dates` section, we learnt how we can use `plus*` and `minus*` 435 | methods to manipulate dates. Those methods are suitable for simple manipulation 436 | operations like adding or subtracting days, months, or years. Sometimes, we need 437 | to perform advance date time manipulation such as adjusting date to first day of 438 | next month or adjusting date to next working day or adjusting date to next 439 | public holiday then we can use `TemporalAdjusters` to meet our needs. Java 8 440 | comes bundled with many predefined temporal adjusters for common scenarios. 441 | These temporal adjusters are available as static factory methods inside the 442 | `TemporalAdjusters` class. 443 | 444 | ```java 445 | LocalDate date = LocalDate.of(2015, Month.OCTOBER, 25); 446 | System.out.println(date);// This will print 2015-10-25 447 | 448 | LocalDate firstDayOfMonth = date.with(TemporalAdjusters.firstDayOfMonth()); 449 | System.out.println(firstDayOfMonth); // This will print 2015-10-01 450 | 451 | LocalDate firstDayOfNextMonth = date.with(TemporalAdjusters.firstDayOfNextMonth()); 452 | System.out.println(firstDayOfNextMonth);// This will print 2015-11-01 453 | 454 | LocalDate lastFridayOfMonth = date.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)); 455 | System.out.println(lastFridayOfMonth); // This will print 2015-10-30 456 | ``` 457 | * **firstDayOfMonth** creates a new date set to first day of the current month. 458 | * **firstDayOfNextMonth** creates a new date set to first day of next month. 459 | * **lastInMonth** creates a new date in the same month with the last matching 460 | day-of-week. For example, last Friday in October. 461 | 462 | I have not covered all the temporal-adjusters please refer to the documentation 463 | for the same. 464 | 465 | ![TemporalAdjusters](https://whyjava.files.wordpress.com/2015/10/temporal-adjusters.png) 466 | 467 | ### Writing custom TemporalAdjuster 468 | 469 | You can write your own adjuster by implementing `TemporalAdjuster` functional 470 | interface. Let's suppose we have to write a TemporalAdjuster that adjusts 471 | today's date to next working date then we can use the `TemporalAdjusters` 472 | `ofDateAdjuster` method to adjust the current date to next working date as show 473 | below. 474 | 475 | ```java 476 | LocalDate today = LocalDate.now(); 477 | TemporalAdjuster nextWorkingDayAdjuster = TemporalAdjusters.ofDateAdjuster(localDate -> { 478 | DayOfWeek dayOfWeek = localDate.getDayOfWeek(); 479 | if (dayOfWeek == DayOfWeek.FRIDAY) { 480 | return localDate.plusDays(3); 481 | } else if (dayOfWeek == DayOfWeek.SATURDAY) { 482 | return localDate.plusDays(2); 483 | } 484 | return localDate.plusDays(1); 485 | }); 486 | System.out.println(today.with(nextWorkingDayAdjuster)); 487 | ``` 488 | 489 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/08-date-time-api)](https://github.com/igrigorik/ga-beacon) 490 | -------------------------------------------------------------------------------- /09-completable-futures.md: -------------------------------------------------------------------------------- 1 | Completable Futures 2 | ------ 3 | 4 | 5 | 6 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/09-completable-futures)](https://github.com/igrigorik/ga-beacon) 7 | -------------------------------------------------------------------------------- /10-nashorn.md: -------------------------------------------------------------------------------- 1 | Nashorn: Run JavaScript on the JVM [![TimeToRead](http://ttr.myapis.xyz/ttr.svg?pageUrl=https://github.com/shekhargulati/java8-the-missing-tutorial/blob/master/10-nashorn.md)](http://ttr.myapis.xyz/) 2 | ----- 3 | 4 | ![Nashorn](https://upload.wikimedia.org/wikipedia/commons/7/7a/Dortmunder_Nashorn_-_Hell_wieherte_der_Hippogryph.jpg) 5 | 6 | Nashorn is a high-performance JavaScript runtime written in Java for the JVM. It 7 | allows developers to embed JavaScript code inside their Java applications and 8 | even use Java classes and methods from their JavaScript code. You can think it 9 | as an alternative to Google's V8 JavaScript engine. It is a successor to Rhino 10 | JavaScript runtime which came bundled with earlier JDK versions. Nashorn is 11 | written from scratch using new language features like JSR 292(Supporting 12 | Dynamically Typed Languages) and `invokedynamic`. 13 | 14 | From the Nashorn documentation: 15 | 16 | > Nashorn uses invokedynamic to implement all of its invocations. If an 17 | > invocation has a Java object receiver, Nashorn attempts to bind the call to an 18 | > appropriate Java method instead of a JavaScript function. Nashorn has full 19 | > discretion about how it resolves methods. As an example, if it can't find a 20 | > field in the receiver, it looks for an equivalent Java Bean method. The result 21 | > is completely transparent for calls from JavaScript to Java. 22 | 23 | Currently, Nashorn supports [ECMAScript 5.1 24 | specification](http://www.ecma-international.org/ecma-262/5.1/) and work is in 25 | progress to support [ECMAScript 26 | 6](http://www.ecma-international.org/ecma-262/6.0/) as well. Few ECMAScript 6 27 | features like `let` and `const` are available in latest JDK 8 updates(40 and 28 | above) and we will cover them later in this chapter. 29 | 30 | In this chapter, we will cover the following: 31 | 32 | * Working with Nashorn command-line 33 | * Accessing Java classes and methods 34 | * Using external JavaScript libraries 35 | * Writing scripts 36 | * Using Nashorn from Java code 37 | * Using Java 8 features like Streams and Lambdas inside JavaScript code 38 | * Turning off Java language access 39 | 40 | 41 | ## Working with Nashorn command-line 42 | 43 | JDK 8 comes bundled with two command-line tools that can be used to work with 44 | Nashorn engine. These two command-line tools are `jrunscript` and `jjs`. `jjs` 45 | is recommended to be used when working with Nashorn so we will only discuss it. 46 | To use `jjs`, you have to add `jjs` to the path. On *nix machines, you can do 47 | that adding a symbolic link as shown below. 48 | 49 | ```bash 50 | $ cd /usr/bin 51 | $ ln -s $JAVA_HOME/bin/jjs jjs 52 | ``` 53 | 54 | Windows users can add `$JAVA_HOME/bin` to the path for easy access. 55 | 56 | Once you have set the symbolic link you can access `jjs` from your terminal. To 57 | check version of `jjs`, run the following command. 58 | 59 | ```bash 60 | $ jjs -v 61 | nashorn 1.8.0_60 62 | jjs> 63 | ``` 64 | 65 | It will render the version and then show `jjs>` prompt. You can view the full 66 | version of `jjs` by using `jjs -fv` command. 67 | 68 | To quit the `jjs` shell, you can use `Ctrl-C`. 69 | 70 | Once you are inside `jjs`, you can execute any JavaScript code as shown below. 71 | 72 | ```bash 73 | jjs> print("learning about Nashorn") 74 | learning about Nashorn 75 | ``` 76 | 77 | You can define functions as shown below. 78 | 79 | ``` 80 | jjs> function add(a,b) {return a + b;} 81 | ``` 82 | 83 | You can call the add function as shown below. 84 | 85 | ``` 86 | jjs> add(5,10) 87 | 15 88 | ``` 89 | 90 | ## Accessing Java classes and methods 91 | 92 | It is very easy to access Java classes from within Nashorn. Assuming you are 93 | inside the `jjs` shell, you can create an instance of HashMap as shown below. 94 | 95 | ```bash 96 | jjs> var HashMap = Java.type("java.util.HashMap") 97 | jjs> var userAndAge = new HashMap() 98 | jjs> userAndAge.put("shekhar",32) 99 | null 100 | jjs> userAndAge.put("rahul",33) 101 | null 102 | jjs> userAndAge.get("shekhar") 103 | 32 104 | ``` 105 | 106 | In the code shown above we have used `Java` global object to create HashMap 107 | object. `Java` global object has `type` method that takes a string with the 108 | fully qualified Java class name, and returns the corresponding `JavaClass` 109 | function object. 110 | 111 | ```bash 112 | jjs> HashMap 113 | [JavaClass java.util.HashMap] 114 | ``` 115 | 116 | The `var userAndAge = new HashMap()` is used to instantiate `java.util.HashMap` 117 | class using the `new` keyword. 118 | 119 | You can access values by either calling the `get` method or using the `[]` 120 | notation as shown below. 121 | 122 | ```bash 123 | jjs> userAndAge["shekhar"] 124 | 32 125 | ``` 126 | 127 | Similarly, you can work with other Java collections. To use an `ArrayList` you 128 | will write code as shown below. 129 | 130 | ```bash 131 | jjs> var List = Java.type("java.util.ArrayList") 132 | jjs> var names = new List() 133 | jjs> names.add("shekhar") 134 | true 135 | jjs> names.add("rahul") 136 | true 137 | jjs> names.add("sameer") 138 | true 139 | jjs> names.get(0) 140 | shekhar 141 | jjs> names[1] 142 | rahul 143 | ``` 144 | 145 | ### Accessing static methods 146 | 147 | To access static methods you have to first get the Java type using `Java.type` 148 | method and then calling method on `JavaClass` function object. 149 | 150 | ```bash 151 | jjs> var UUID = Java.type("java.util.UUID") 152 | jjs> 153 | jjs> UUID.randomUUID().toString() 154 | e4242b89-0e94-458e-b501-2fc4344d5498 155 | ``` 156 | 157 | You can sort list using `Collections.sort` method as shown below. 158 | 159 | ```bash 160 | jjs> var Collections = Java.type("java.util.Collections") 161 | jjs> 162 | jjs> Collections.sort(names) 163 | jjs> names 164 | [rahul, sameer, shekhar] 165 | jjs> 166 | ``` 167 | 168 | ## Using external JavaScript libraries 169 | 170 | Let's suppose we want to use an external JavaScript library in our JavaScript 171 | code. Nashorn comes up with a built-in function -- `load` that loads and 172 | evaluates a script from a path, URL, or script object. To use `lodash` library 173 | we can write code as shown below. 174 | 175 | ``` 176 | jjs> load("https://raw.github.com/lodash/lodash/3.10.1/lodash.js") 177 | 178 | jjs> _.map([1, 2, 3], function(n) { return n * 3; }); 179 | 3,6,9 180 | ``` 181 | 182 | ## Writing scripts 183 | 184 | You can use Nashorn extensions that enable users to write scripts that can use 185 | Unix shell scripting features. To enable shell scripting features, you have to 186 | start `jjs` with `-scripting` option as shown below. 187 | 188 | ```bash 189 | jjs -scripting 190 | jjs> 191 | ``` 192 | 193 | Now you have access to Nashorn shell scripting global objects. 194 | 195 | **$ARG:** This global object can be used to access the arguments passed to the 196 | script 197 | 198 | ``` 199 | $ jjs -scripting -- hello hey 200 | jjs> 201 | jjs> $ARG 202 | hello,hey 203 | ``` 204 | 205 | **$ENV:** A map containing all the current environment variables 206 | 207 | ```bash 208 | jjs> $ENV["HOME"] 209 | /Users/shekhargulati 210 | 211 | jjs> $ENV.JAVA_HOME 212 | /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home 213 | ``` 214 | 215 | **$EXEC:** launches processes to run commands 216 | 217 | ```bash 218 | jjs> $EXEC("pwd") 219 | /Users/shekhargulati/java8-the-missing-tutorial 220 | ``` 221 | 222 | ### Writing executable scripts 223 | 224 | You can use shebang(#!) at the beginning of the script to make a script file run 225 | as shell executable. Let's write a simple script that reads content of a file. 226 | We will use Java's `Files` and `Paths` API. 227 | 228 | ```javascript 229 | #!/usr/bin/jjs 230 | 231 | var Paths = Java.type("java.nio.file.Paths"); 232 | var Files = Java.type("java.nio.file.Files"); 233 | 234 | Files.lines(Paths.get($ARG[0])).forEach(function(line){print(line);}) 235 | ``` 236 | 237 | We will invoke it as 238 | 239 | ```bash 240 | $ jjs ch10/lines.js -- README.md 241 | ``` 242 | 243 | ## Using Nashorn from Java code 244 | 245 | To use Nashorn from inside Java code, you have to create an instance of 246 | ScriptEngine from `ScriptEngineManager` as shown below. Once you have 247 | `ScriptEngine` you can evaluate expressions. 248 | 249 | ```java 250 | import javax.script.ScriptEngine; 251 | import javax.script.ScriptEngineManager; 252 | import javax.script.ScriptException; 253 | 254 | public class NashornExample1 { 255 | 256 | public static void main(String[] args) throws ScriptException { 257 | ScriptEngineManager manager = new ScriptEngineManager(); 258 | ScriptEngine nashorn = manager.getEngineByName("nashorn"); 259 | Integer eval = (Integer) nashorn.eval("10 + 20"); 260 | System.out.println(eval); 261 | } 262 | } 263 | ``` 264 | 265 | Using bindings 266 | 267 | ```java 268 | import javax.script.*; 269 | import java.util.AbstractMap.SimpleEntry; 270 | import java.util.stream.Stream; 271 | 272 | import static java.util.stream.Collectors.toMap; 273 | 274 | public class NashornExample2 { 275 | 276 | public static void main(String[] args) throws ScriptException { 277 | ScriptEngineManager manager = new ScriptEngineManager(); 278 | ScriptEngine nashorn = manager.getEngineByName("nashorn"); 279 | 280 | Bindings bindings = new SimpleBindings(Stream.of( 281 | new SimpleEntry<>("a", 10), 282 | new SimpleEntry<>("b", 20)) 283 | .collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue))); 284 | Double eval = (Double) nashorn.eval("a + b", bindings); 285 | System.out.println(eval); 286 | } 287 | } 288 | ``` 289 | 290 | ## Using Java 8 features like Streams and Lambdas inside JavaScript code 291 | 292 | Java 8 supports lambdas and many API in JDK make use of them. Every collection 293 | in Java has `forEach` method that accepts a consumer. Consumer is an interface 294 | with one method. In Java, you can write following: 295 | 296 | ```java 297 | Arrays.asList("shekhar","rahul","sameer").forEach(name -> System.out.println(name)); 298 | 299 | // shekhar 300 | // rahul 301 | // sameer 302 | ``` 303 | 304 | In Nashorn, you can use them same API but you will pass JavaScript function 305 | instead as shown below. 306 | 307 | ```javascript 308 | jjs> var Arrays = Java.type("java.util.Arrays") 309 | jjs> Arrays.asList("shekhar","rahul","sameer") 310 | [shekhar, rahul, sameer] 311 | jjs> var names = Arrays.asList("shekhar","rahul","sameer") 312 | jjs> names.forEach(function(name){print(name);}) 313 | shekhar 314 | rahul 315 | sameer 316 | ``` 317 | 318 | You can also use Stream API with Nashorn as shown below. 319 | 320 | ``` 321 | jjs> names 322 | .stream().filter(function(name){return name.startsWith("s");}) 323 | .forEach(function(name){print(name);}) 324 | 325 | shekhar 326 | sameer 327 | ``` 328 | 329 | ## Turning off Java language access 330 | 331 | In case you need to disallow Java usage, you can very easily turn off by passing 332 | `--no-java` option to `jjs` as shown below. 333 | 334 | ``` 335 | → jjs --no-java 336 | jjs> 337 | jjs> var HashMap = Java.type("java.util.HashMap") 338 | :1 TypeError: null has no such function "type" 339 | ``` 340 | 341 | Now when you will try to use `java.util.HashMap` you will get `TypeError`. 342 | 343 | You can do the same with Java code as well. 344 | 345 | ```java 346 | 347 | import jdk.nashorn.api.scripting.ClassFilter; 348 | import jdk.nashorn.api.scripting.NashornScriptEngineFactory; 349 | 350 | import javax.script.ScriptEngine; 351 | import javax.script.ScriptException; 352 | 353 | public class NashornExample3 { 354 | 355 | public static void main(String[] args) throws ScriptException { 356 | NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); 357 | ScriptEngine nashorn = factory.getScriptEngine(new NoJavaFilter()); 358 | Integer eval = (Integer) nashorn.eval("var HashMap = Java.type('java.util.HashMap')"); 359 | System.out.println(eval); 360 | } 361 | 362 | private static class NoJavaFilter implements ClassFilter{ 363 | 364 | @Override 365 | public boolean exposeToScripts(String s) { 366 | return false; 367 | } 368 | } 369 | } 370 | ``` 371 | 372 | You will get following exception when you run this program. 373 | 374 | ``` 375 | Caused by: java.lang.ClassNotFoundException: java.util.HashMap 376 | at jdk.nashorn.internal.runtime.Context.findClass(Context.java:1032) 377 | at jdk.nashorn.internal.objects.NativeJava.simpleType(NativeJava.java:493) 378 | at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:322) 379 | at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:314) 380 | at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:310) 381 | at jdk.nashorn.internal.scripts.Script$\^eval\_.:program(:1) 382 | at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:640) 383 | at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:228) 384 | at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393) 385 | ``` 386 | 387 | 388 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/10-nashorn)](https://github.com/igrigorik/ga-beacon) 389 | -------------------------------------------------------------------------------- /11-tools.md: -------------------------------------------------------------------------------- 1 | Tools 2 | ----- 3 | 4 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/11-tools)](https://github.com/igrigorik/ga-beacon) 5 | -------------------------------------------------------------------------------- /12-annotations.md: -------------------------------------------------------------------------------- 1 | Annotation Improvements [![TimeToRead](http://ttr.myapis.xyz/ttr.svg?pageUrl=https://github.com/shekhargulati/java8-the-missing-tutorial/blob/master/12-annotations.md)](http://ttr.myapis.xyz/) 2 | ------- 3 | 4 | There are couple of improvements made in the annotation mechanism in Java 8. 5 | These are: 6 | 7 | 1. Repeatable annotations 8 | 2. Type annotations 9 | 10 | > Code for this section is inside [ch12 package](https://github.com/shekhargulati/java8-the-missing-tutorial/tree/master/code/src/main/java/com/shekhargulati/java8_tutorial/ch12). 11 | 12 | ## Repeatable annotations 13 | 14 | Before Java 8, it was not possible to use same annotation twice at the same 15 | location i.e. you can't use annotation `@Foo` twice on a method. If you have 16 | used JPA then you would have use an annotation called `@JoinColumns` that allows 17 | wraps multiple `@JoinColumn` annotation as shown below. 18 | 19 | ```java 20 | @ManyToOne 21 | @JoinColumns({ 22 | @JoinColumn(name="ADDR_ID", referencedColumnName="ID"), 23 | @JoinColumn(name="ADDR_ZIP", referencedColumnName="ZIP") 24 | }) 25 | public Address getAddress() { return address; } 26 | ``` 27 | 28 | In Java 8, you can write the same as shown below because with Java 8 you can repeat an annotation multiple time at the same location. 29 | 30 | ```java 31 | @ManyToOne 32 | @JoinColumn(name="ADDR_ID", referencedColumnName="ID"), 33 | @JoinColumn(name="ADDR_ZIP", referencedColumnName="ZIP") 34 | public Address getAddress() { return address; } 35 | ``` 36 | 37 | ***With Java 8 you can use same annotation type multiple times possibly with 38 | different arguments at any location(class, method, field declaration) in your 39 | Java code.*** 40 | 41 | ### Writing your own repeatable Annotations 42 | 43 | To write your own repeatable annotation you have to do the following: 44 | 45 | **Step 1:** Create an annotation with `@Repeatable` annotation as shown below. 46 | `@Repeatable` annotation requires you to specify a mandatory value for the 47 | container type that will contain the annotation. We will create container 48 | annotation in step 2. 49 | 50 | ```java 51 | import java.lang.annotation.*; 52 | 53 | @Retention(RetentionPolicy.RUNTIME) 54 | @Target(ElementType.METHOD) 55 | @Repeatable(CreateVms.class) 56 | public @interface CreateVm { 57 | String name(); 58 | } 59 | ``` 60 | 61 | **Step 2:** Create a container annotation that holds the annotation. 62 | 63 | ```java 64 | @Retention(RetentionPolicy.RUNTIME) 65 | @Target(ElementType.METHOD) 66 | @interface CreateVms { 67 | CreateVm[] value(); 68 | } 69 | ``` 70 | 71 | Now you can use the annotation multiple times on any method as shown below. 72 | 73 | ```java 74 | @CreateVm(name = "vm1") 75 | @CreateVm(name = "vm2") 76 | public void manage() { 77 | System.out.println("Managing ...."); 78 | } 79 | ``` 80 | 81 | If you have to find all the repeatable annotations on a method then you can use 82 | `getAnnotationsByType` method that is now available on `java.lang.Class` and 83 | `java.lang.reflect.Method`. To print all the vm names, you can write code as 84 | shown below. 85 | 86 | ```java 87 | CreateVm[] createVms = VmManager.class.getMethod("manage").getAnnotationsByType(CreateVm.class); 88 | Stream.of(createVms).map(CreateVm::name).forEach(System.out::println); 89 | ``` 90 | 91 | ## Type annotations 92 | 93 | You can now apply annotations at two more target locations -- TYPE_PARAMETER and 94 | TYPE_USE. 95 | 96 | ```java 97 | @Retention(RetentionPolicy.RUNTIME) 98 | @Target(value = {ElementType.TYPE_PARAMETER}) 99 | public @interface MyAnnotation { 100 | } 101 | ``` 102 | 103 | You can use it like 104 | 105 | ```java 106 | class MyAnnotationUsage<@MyAnnotation T> { 107 | } 108 | ``` 109 | 110 | You can also use annotations at type usage as shown below. 111 | 112 | ```java 113 | @Retention(RetentionPolicy.RUNTIME) 114 | @Target(value = {ElementType.TYPE_USE}) 115 | public @interface MyAnnotation { 116 | } 117 | ``` 118 | 119 | Then you can use them as shown below. 120 | 121 | ```java 122 | public static void doSth() { 123 | List<@MyAnnotation String> names = Arrays.asList("shekhar"); 124 | } 125 | ``` 126 | 127 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/12-annotations)](https://github.com/igrigorik/ga-beacon) 128 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Shekhar Gulati 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Java 8: The Missing Tutorial 2 | -------------- 3 | 4 | Java 8 is not a new topic anymore. There are many good books published on it. Still I meet many Java developers unaware of the power of Java 8. The goal of this microbook is to cover some of the most important Java 8 features and how they can help developers in their day to day programming. This is based on my [7 days with Java 8](http://shekhargulati.com/7-days-with-java-8/) blog series. 5 | 6 | ## Contributing to the Java 8: The Missing Tutorial 7 | 8 | Please contribute if you see an error or something that could be better! Raise an [issue](https://github.com/shekhargulati/java8-the-missing-tutorial/issues) or send me a pull request to improve. Contributions of all kinds, including corrections, additions, improvements, and translations, are welcome! 9 | 10 | ## Table of Contents 11 | 12 | * [Default and Static Methods for Interfaces](./01-default-static-interface-methods.md) 13 | * [Lambdas](./02-lambdas.md) 14 | * [Streams](./03-streams.md) 15 | * [Collectors](./04-collectors.md) 16 | * [Optionals](./05-optionals.md) 17 | * [Date Time API](./08-date-time-api.md) 18 | * [Nashorn](./10-nashorn.md) 19 | * [Annotation improvements](./12-annotations.md) 20 | 21 | ----------- 22 | You can follow me on twitter at [https://twitter.com/shekhargulati](https://twitter.com/shekhargulati) or email me at . Also, you can read my blogs at [http://shekhargulati.com/](http://shekhargulati.com/) 23 | 24 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial)](https://github.com/igrigorik/ga-beacon) 25 | -------------------------------------------------------------------------------- /ch10/lines.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/jjs 2 | 3 | var Paths = Java.type("java.nio.file.Paths"); 4 | var Files = Java.type("java.nio.file.Files"); 5 | 6 | Files.lines(Paths.get($ARG[0])).forEach(function(line){print(line);}) 7 | -------------------------------------------------------------------------------- /code/.gitignore: -------------------------------------------------------------------------------- 1 | ### JetBrains template 2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 3 | 4 | *.iml 5 | 6 | ## Directory-based project format: 7 | .idea/ 8 | # if you remove the above rule, at least ignore the following: 9 | 10 | # User-specific stuff: 11 | # .idea/workspace.xml 12 | # .idea/tasks.xml 13 | # .idea/dictionaries 14 | 15 | # Sensitive or high-churn files: 16 | # .idea/dataSources.ids 17 | # .idea/dataSources.xml 18 | # .idea/sqlDataSources.xml 19 | # .idea/dynamic.xml 20 | # .idea/uiDesigner.xml 21 | 22 | # Gradle: 23 | # .idea/gradle.xml 24 | # .idea/libraries 25 | 26 | # Mongo Explorer plugin: 27 | # .idea/mongoSettings.xml 28 | 29 | ## File-based project format: 30 | *.ipr 31 | *.iws 32 | 33 | ## Plugin-specific files: 34 | 35 | # IntelliJ 36 | /out/ 37 | 38 | # mpeltonen/sbt-idea plugin 39 | .idea_modules/ 40 | 41 | # JIRA plugin 42 | atlassian-ide-plugin.xml 43 | 44 | # Crashlytics plugin (for Android Studio and IntelliJ) 45 | com_crashlytics_export_strings.xml 46 | crashlytics.properties 47 | crashlytics-build.properties 48 | 49 | # Created by .ignore support plugin (hsz.mobi) 50 | ### Java template 51 | *.class 52 | 53 | # Mobile Tools for Java (J2ME) 54 | .mtj.tmp/ 55 | 56 | # Package Files # 57 | *.jar 58 | *.war 59 | *.ear 60 | 61 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 62 | hs_err_pid* 63 | ### Gradle template 64 | .gradle 65 | build/ 66 | 67 | # Ignore Gradle GUI config 68 | gradle-app.setting 69 | 70 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 71 | !gradle-wrapper.jar 72 | 73 | -------------------------------------------------------------------------------- /code/README.md: -------------------------------------------------------------------------------- 1 | Java 8: The Missing Tutorial Supporting Code 2 | ----- 3 | 4 | This gradle Java 8 project houses example code that is used in the tutorial. Please refer to Table of Content to go to their respective packages inside the Java project. 5 | 6 | ## Table of Contents 7 | 8 | * [Default and Static Methods for Interfaces](https://github.com/shekhargulati/java8-the-missing-tutorial/tree/master/code/src/main/java/com/shekhargulati/java8_tutorial/ch01) 9 | -------------------------------------------------------------------------------- /code/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.shekhargulati' 2 | version '1.0-SNAPSHOT' 3 | 4 | apply plugin: 'java' 5 | 6 | sourceCompatibility = 1.8 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testCompile "org.hamcrest:hamcrest-all:1.3" 14 | testCompile group: 'junit', name: 'junit', version: '4.12' 15 | } 16 | -------------------------------------------------------------------------------- /code/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/java8-the-missing-tutorial/bb31c1d7d2bb60589f294c896c044b763c97a906/code/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /code/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Nov 22 18:13:21 IST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-bin.zip 7 | -------------------------------------------------------------------------------- /code/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /code/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /code/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'java8-the-missing-tutorial' 2 | 3 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch01/App1.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch01; 2 | 3 | public class App1 implements A { 4 | @Override 5 | public void doSth() { 6 | System.out.println("inside App1"); 7 | } 8 | 9 | public static void main(String[] args) { 10 | new App1().doSth(); 11 | } 12 | } 13 | 14 | interface A { 15 | default void doSth() { 16 | System.out.println("inside A"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch01/App2.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch01; 2 | 3 | public class App2 implements B, D { 4 | public static void main(String[] args) { 5 | new App2().doSth(); 6 | } 7 | } 8 | 9 | interface B { 10 | default void doSth() { 11 | System.out.println("inside B"); 12 | } 13 | } 14 | 15 | interface D extends B { 16 | default void doSth() { 17 | System.out.println("inside D"); 18 | } 19 | } 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch01/App3.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch01; 2 | 3 | public class App3 implements E, F { 4 | @Override 5 | public void doSth() { 6 | F.super.doSth(); 7 | } 8 | 9 | public static void main(String[] args) { 10 | new App3().doSth(); 11 | } 12 | } 13 | 14 | interface E { 15 | default void doSth() { 16 | System.out.println("inside E"); 17 | } 18 | } 19 | 20 | interface F { 21 | default void doSth() { 22 | System.out.println("inside F"); 23 | } 24 | } -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch01/BasicCalculator.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch01; 2 | 3 | 4 | class BasicCalculator implements Calculator { 5 | 6 | @Override 7 | public int add(int first, int second) { 8 | return first + second; 9 | } 10 | 11 | @Override 12 | public int subtract(int first, int second) { 13 | return first - second; 14 | } 15 | 16 | @Override 17 | public int divide(int number, int divisor) { 18 | if (divisor == 0) { 19 | throw new IllegalArgumentException("divisor can't be zero."); 20 | } 21 | return number / divisor; 22 | } 23 | 24 | @Override 25 | public int multiply(int first, int second) { 26 | return first * second; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch01/Calculator.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch01; 2 | 3 | public interface Calculator { 4 | 5 | static Calculator getInstance() { 6 | return new BasicCalculator(); 7 | } 8 | 9 | int add(int first, int second); 10 | 11 | int subtract(int first, int second); 12 | 13 | int divide(int number, int divisor); 14 | 15 | int multiply(int first, int second); 16 | 17 | default int remainder(int number, int divisor) { 18 | return subtract(number, multiply(divisor, divide(number, divisor))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch01/CalculatorFactory.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch01; 2 | 3 | public abstract class CalculatorFactory { 4 | 5 | public static Calculator getInstance() { 6 | return new BasicCalculator(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch02/Example1_Lambda.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch02; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.Comparator; 6 | import java.util.List; 7 | 8 | public class Example1_Lambda { 9 | 10 | public static void main(String[] args) { 11 | List names = Arrays.asList("shekhar", "rahul", "sameer"); 12 | // sort alphabetically 13 | Collections.sort(names); 14 | System.out.println("names sorted alphabetically >>"); 15 | System.out.println(names); 16 | System.out.println(); 17 | 18 | // using anonymous classes 19 | Collections.sort(names, new Comparator() { 20 | @Override 21 | public int compare(String o1, String o2) { 22 | return o1.length() - o2.length(); 23 | } 24 | }); 25 | System.out.println("names sorted by length >>"); 26 | System.out.println(names); 27 | System.out.println(); 28 | 29 | /** 30 | * Using lambda 31 | * Things to show >> 32 | * 1. return statement 33 | * 2. Without return statement 34 | * 3. Multiple lines 35 | * 4. Type inference 36 | */ 37 | 38 | Collections.sort(names, (String first, String second) -> second.length() - first.length()); 39 | System.out.println("names sorted by length(reversed) >>"); 40 | System.out.println(names); 41 | System.out.println(); 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch02/Example2_Lambda.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch02; 2 | 3 | /** 4 | * 5 | * Example of compiler unable to detect lambda expression types 6 | */ 7 | public class Example2_Lambda { 8 | 9 | public static void main(String[] args) { 10 | 11 | // Comparator comparator = (first, second) -> first.length() - second.length(); 12 | 13 | 14 | } 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch02/Example3_Functionalnterfaces.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch02; 2 | 3 | import java.util.UUID; 4 | import java.util.function.Consumer; 5 | import java.util.function.Function; 6 | import java.util.function.Predicate; 7 | import java.util.function.Supplier; 8 | 9 | public class Example3_Functionalnterfaces { 10 | 11 | public static void main(String[] args) { 12 | 13 | Predicate nameStartWithS = name -> name.startsWith("s"); 14 | 15 | Consumer sendEmail = message -> System.out.println("Sending email >> " + message); 16 | 17 | Function stringToLength = name -> name.length(); 18 | 19 | Supplier uuidSupplier = () -> UUID.randomUUID().toString(); 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch02/Example4_MethodReferences.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch02; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import java.util.function.Function; 8 | 9 | import static java.util.Comparator.comparingInt; 10 | 11 | public class Example4_MethodReferences { 12 | 13 | public static void main(String[] args) { 14 | List names = Arrays.asList("shekhar", "rahul", "sameer"); 15 | 16 | List namesLength = transform(names, String::length); 17 | System.out.println(namesLength); 18 | 19 | List upperCaseNames = transform(names, String::toUpperCase); 20 | System.out.println(upperCaseNames); 21 | 22 | List numbers = transform(Arrays.asList("1", "2", "3"), Integer::parseInt); 23 | System.out.println(numbers); 24 | 25 | Collections.sort(names, comparingInt(String::length).reversed()); 26 | System.out.println(names); 27 | } 28 | 29 | private static List transform(List list, Function fx) { 30 | List result = new ArrayList<>(); 31 | for (T element : list) { 32 | result.add(fx.apply(element)); 33 | } 34 | return result; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch02/InvalidFunctionInterface.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch02; 2 | 3 | /** 4 | * Example of @FunctionalInterface 5 | */ 6 | 7 | @FunctionalInterface 8 | public interface InvalidFunctionInterface { 9 | 10 | public boolean test(); 11 | 12 | // public boolean test1(); 13 | 14 | 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch03/Example1_Java7.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch03; 2 | 3 | 4 | import com.shekhargulati.java8_tutorial.domain.Task; 5 | import com.shekhargulati.java8_tutorial.domain.TaskType; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.Comparator; 10 | import java.util.List; 11 | 12 | import static com.shekhargulati.java8_tutorial.utils.DataUtils.getTasks; 13 | 14 | 15 | public class Example1_Java7 { 16 | 17 | public static void main(String[] args) { 18 | List tasks = getTasks(); 19 | List readingTasks = new ArrayList<>(); 20 | for (Task task : tasks) { 21 | if (task.getType() == TaskType.READING) { 22 | readingTasks.add(task); 23 | } 24 | } 25 | Collections.sort(readingTasks, new Comparator() { 26 | @Override 27 | public int compare(Task t1, Task t2) { 28 | return t1.getTitle().length() - t2.getTitle().length(); 29 | } 30 | }); 31 | for (Task readingTask : readingTasks) { 32 | System.out.println(readingTask.getTitle()); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch03/Example1_Stream.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch03; 2 | 3 | 4 | import com.shekhargulati.java8_tutorial.domain.Task; 5 | import com.shekhargulati.java8_tutorial.domain.TaskType; 6 | 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | import static com.shekhargulati.java8_tutorial.utils.DataUtils.getTasks; 11 | 12 | 13 | public class Example1_Stream { 14 | 15 | public static void main(String[] args) { 16 | List tasks = getTasks(); 17 | 18 | List readingTasks = tasks.stream() 19 | .filter(task -> task.getType() == TaskType.READING) 20 | .sorted((t1, t2) -> t1.getTitle().length() - t2.getTitle().length()) 21 | .map(Task::getTitle) 22 | .collect(Collectors.toList()); 23 | 24 | readingTasks.forEach(System.out::println); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch03/Examples.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch03; 2 | 3 | import com.shekhargulati.java8_tutorial.domain.Task; 4 | import com.shekhargulati.java8_tutorial.domain.TaskType; 5 | 6 | import java.util.List; 7 | 8 | import static java.util.Comparator.comparing; 9 | import static java.util.stream.Collectors.toList; 10 | 11 | public class Examples { 12 | 13 | public List allReadingTasks(List tasks) { 14 | return tasks.stream(). 15 | filter(task -> task.getType() == TaskType.READING). 16 | sorted(comparing(Task::getCreatedOn)). 17 | map(Task::getTitle). 18 | collect(toList()); 19 | 20 | } 21 | 22 | 23 | public List allReadingTasksSortedByCreatedOnDesc(List tasks) { 24 | return tasks.stream(). 25 | filter(task -> task.getType() == TaskType.READING). 26 | sorted(comparing(Task::getCreatedOn).reversed()). 27 | map(Task::getTitle). 28 | collect(toList()); 29 | 30 | } 31 | 32 | public List allDistinctTasks(List tasks) { 33 | return tasks.stream().distinct().collect(toList()); 34 | } 35 | 36 | public List topN(List tasks, int n) { 37 | return tasks.stream(). 38 | filter(task -> task.getType() == TaskType.READING). 39 | sorted(comparing(Task::getCreatedOn)). 40 | map(Task::getTitle). 41 | limit(n). 42 | collect(toList()); 43 | } 44 | 45 | public long countAllReadingTasks(List tasks) { 46 | return tasks.stream(). 47 | filter(task -> task.getType() == TaskType.READING). 48 | count(); 49 | } 50 | 51 | public List allDistinctTags(List tasks) { 52 | return tasks.stream().flatMap(task -> task.getTags().stream()).distinct().collect(toList()); 53 | } 54 | 55 | public boolean isAllReadingTasksWithTagBooks(List tasks) { 56 | return tasks.stream(). 57 | filter(task -> task.getType() == TaskType.READING). 58 | allMatch(task -> task.getTags().contains("books")); 59 | } 60 | 61 | public boolean isAnyReadingTasksWithTagJava8(List tasks) { 62 | return tasks.stream(). 63 | filter(task -> task.getType() == TaskType.READING). 64 | anyMatch(task -> task.getTags().contains("java8")); 65 | } 66 | 67 | public String joinAllTaskTitles(List tasks) { 68 | return tasks.stream(). 69 | map(Task::getTitle). 70 | reduce((first, second) -> first + " *** " + second). 71 | get(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch03/ParallelStreamExample.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch03; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.stream.IntStream; 6 | 7 | import static java.util.stream.Collectors.groupingBy; 8 | 9 | public class ParallelStreamExample { 10 | 11 | public static void main(String[] args) { 12 | Map> numbersPerThread = IntStream.rangeClosed(1, 160) 13 | .parallel() 14 | .boxed() 15 | .collect(groupingBy(i -> Thread.currentThread().getName())); 16 | 17 | numbersPerThread.forEach((k, v) -> System.out.println(String.format("%s >> %s", k, v))); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch05/TaskNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch05; 2 | 3 | public class TaskNotFoundException extends RuntimeException { 4 | public TaskNotFoundException(String id) { 5 | super("No task found for id: " + id); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch05/TaskRepository.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch05; 2 | 3 | 4 | 5 | import com.shekhargulati.java8_tutorial.ch05.domain.Task; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.Optional; 10 | 11 | public class TaskRepository { 12 | 13 | private final Map db = new HashMap<>(); 14 | 15 | public void loadData() { 16 | db.put("1", new Task("1", "hello java 1")); 17 | db.put("2", new Task("2", "hello java 2")); 18 | db.put("3", new Task("3", "hello java 3")); 19 | db.put("4", new Task("4", "hello java 4")); 20 | db.put("5", new Task("5", "hello java 5")); 21 | } 22 | 23 | public Task find(String id) { 24 | return Optional.ofNullable(db.get(id)) 25 | .orElseThrow(() -> new TaskNotFoundException(id)); 26 | } 27 | 28 | public Optional taskAssignedTo(String id) { 29 | return Optional.ofNullable(find(id)) 30 | .flatMap(task -> task.getAssignedTo()) 31 | .map(user -> user.getUsername()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch05/domain/Task.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch05.domain; 2 | 3 | import java.util.Optional; 4 | 5 | public class Task { 6 | private final String title; 7 | private final Optional assignedTo; 8 | private final String id; 9 | 10 | public Task(String id, String title) { 11 | this.id = id; 12 | this.title = title; 13 | assignedTo = Optional.empty(); 14 | } 15 | 16 | public Task(String id, String title, User assignedTo) { 17 | this.id = id; 18 | this.title = title; 19 | this.assignedTo = Optional.ofNullable(assignedTo); 20 | } 21 | 22 | public String getId() { 23 | return id; 24 | } 25 | 26 | public String getTitle() { 27 | return title; 28 | } 29 | 30 | public Optional getAssignedTo() { 31 | return assignedTo; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch05/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch05.domain; 2 | 3 | import java.util.Optional; 4 | 5 | public class User { 6 | 7 | private final String username; 8 | private final Optional address; 9 | 10 | public User(String username) { 11 | this.username = username; 12 | this.address = Optional.empty(); 13 | } 14 | 15 | public User(String username, String address) { 16 | this.username = username; 17 | this.address = Optional.ofNullable(address); 18 | } 19 | 20 | public String getUsername() { 21 | return username; 22 | } 23 | 24 | public Optional getAddress() { 25 | return address; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch06/MapExample.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch06; 2 | 3 | import java.util.AbstractMap.SimpleEntry; 4 | import java.util.Map; 5 | import java.util.stream.Stream; 6 | 7 | import static java.util.stream.Collectors.toMap; 8 | 9 | public class MapExample { 10 | 11 | public static Map createMap(SimpleEntry... entries) { 12 | return Stream.of(entries).collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue)); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch09/CompletableFutureExample.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch09; 2 | 3 | import java.util.UUID; 4 | import java.util.concurrent.CompletableFuture; 5 | import java.util.concurrent.Executors; 6 | 7 | public class CompletableFutureExample { 8 | 9 | public static void main(String[] args) { 10 | CompletableFuture.completedFuture("hello"); 11 | CompletableFuture.runAsync(() -> System.out.println("hello")); 12 | CompletableFuture.runAsync(() -> System.out.println("hello"), Executors.newSingleThreadExecutor()); 13 | CompletableFuture.supplyAsync(() -> UUID.randomUUID().toString()); 14 | CompletableFuture.supplyAsync(() -> UUID.randomUUID().toString(), Executors.newSingleThreadExecutor()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch10/NashornExample1.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch10; 2 | 3 | import javax.script.ScriptEngine; 4 | import javax.script.ScriptEngineManager; 5 | import javax.script.ScriptException; 6 | 7 | public class NashornExample1 { 8 | 9 | public static void main(String[] args) throws ScriptException { 10 | ScriptEngineManager manager = new ScriptEngineManager(); 11 | ScriptEngine nashorn = manager.getEngineByName("nashorn"); 12 | Integer eval = (Integer) nashorn.eval("10 + 20"); 13 | System.out.println(eval); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch10/NashornExample2.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch10; 2 | 3 | import javax.script.*; 4 | import java.util.AbstractMap.SimpleEntry; 5 | import java.util.stream.Stream; 6 | 7 | import static java.util.stream.Collectors.toMap; 8 | 9 | public class NashornExample2 { 10 | 11 | public static void main(String[] args) throws ScriptException { 12 | ScriptEngineManager manager = new ScriptEngineManager(); 13 | ScriptEngine nashorn = manager.getEngineByName("nashorn"); 14 | 15 | Bindings bindings = new SimpleBindings(Stream.of( 16 | new SimpleEntry<>("a", 10), 17 | new SimpleEntry<>("b", 20)) 18 | .collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue))); 19 | Double eval = (Double) nashorn.eval("a + b", bindings); 20 | System.out.println(eval); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch10/NashornExample3.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch10; 2 | 3 | import jdk.nashorn.api.scripting.ClassFilter; 4 | import jdk.nashorn.api.scripting.NashornScriptEngineFactory; 5 | 6 | import javax.script.ScriptEngine; 7 | import javax.script.ScriptException; 8 | 9 | public class NashornExample3 { 10 | 11 | public static void main(String[] args) throws ScriptException { 12 | NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); 13 | ScriptEngine nashorn = factory.getScriptEngine(new NoJavaFilter()); 14 | Integer eval = (Integer) nashorn.eval("var HashMap = Java.type('java.util.HashMap')"); 15 | System.out.println(eval); 16 | } 17 | 18 | private static class NoJavaFilter implements ClassFilter{ 19 | 20 | @Override 21 | public boolean exposeToScripts(String s) { 22 | return false; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch12/CreateVm.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch12; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Retention(RetentionPolicy.RUNTIME) 6 | @Target(ElementType.METHOD) 7 | @Repeatable(CreateVms.class) 8 | public @interface CreateVm { 9 | String name(); 10 | } 11 | 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target(ElementType.METHOD) 14 | @interface CreateVms { 15 | CreateVm[] value(); 16 | } -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch12/MyAnnotation.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch12; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target(value = {ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) 12 | public @interface MyAnnotation { 13 | } 14 | 15 | 16 | class MyAnnotationUsage<@MyAnnotation T> { 17 | 18 | public static void main(String[] args) { 19 | 20 | } 21 | 22 | public static void doSth() { 23 | List<@MyAnnotation String> names = Arrays.asList("shekhar"); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/ch12/VmManager.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch12; 2 | 3 | import java.util.stream.Stream; 4 | 5 | public class VmManager { 6 | 7 | @CreateVm(name = "vm1") 8 | @CreateVm(name = "vm2") 9 | public void manage() { 10 | System.out.println("Managing ...."); 11 | } 12 | 13 | public static void main(String[] args) throws Exception { 14 | CreateVm[] createVms = VmManager.class.getMethod("manage").getAnnotationsByType(CreateVm.class); 15 | Stream.of(createVms).map(CreateVm::name).forEach(System.out::println); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/domain/Task.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.domain; 2 | 3 | import java.time.LocalDate; 4 | import java.util.*; 5 | 6 | public class Task { 7 | 8 | private final String id; 9 | private final String title; 10 | private final String description; 11 | private final TaskType type; 12 | private LocalDate createdOn; 13 | private Set tags = new HashSet<>(); 14 | 15 | public Task(final String id, final String title, final TaskType type) { 16 | this.id = id; 17 | this.title = title; 18 | this.description = title; 19 | this.type = type; 20 | this.createdOn = LocalDate.now(); 21 | } 22 | 23 | public Task(final String title, final TaskType type) { 24 | this(title, title, type, LocalDate.now()); 25 | } 26 | 27 | public Task(final String title, final TaskType type, final LocalDate createdOn) { 28 | this(title, title, type, createdOn); 29 | } 30 | 31 | public Task(final String title, final String description, final TaskType type, final LocalDate createdOn) { 32 | this.id = UUID.randomUUID().toString(); 33 | this.title = title; 34 | this.description = description; 35 | this.type = type; 36 | this.createdOn = createdOn; 37 | } 38 | 39 | public String getId() { 40 | return id; 41 | } 42 | 43 | public String getTitle() { 44 | return title; 45 | } 46 | 47 | public String getDescription() { 48 | return description; 49 | } 50 | 51 | public TaskType getType() { 52 | return type; 53 | } 54 | 55 | public LocalDate getCreatedOn() { 56 | return createdOn; 57 | } 58 | 59 | public Task addTag(String tag) { 60 | this.tags.add(tag); 61 | return this; 62 | } 63 | 64 | public Set getTags() { 65 | return Collections.unmodifiableSet(tags); 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return "Task{" + 71 | "title='" + title + '\'' + 72 | ", type=" + type + 73 | '}'; 74 | } 75 | 76 | @Override 77 | public boolean equals(Object o) { 78 | if (this == o) return true; 79 | if (o == null || getClass() != o.getClass()) return false; 80 | Task task = (Task) o; 81 | return Objects.equals(title, task.title) && 82 | Objects.equals(type, task.type); 83 | } 84 | 85 | @Override 86 | public int hashCode() { 87 | return Objects.hash(title, type); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/domain/TaskType.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.domain; 2 | 3 | public enum TaskType { 4 | 5 | READING, CODING, BLOGGING 6 | } 7 | -------------------------------------------------------------------------------- /code/src/main/java/com/shekhargulati/java8_tutorial/utils/DataUtils.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.utils; 2 | 3 | 4 | import com.shekhargulati.java8_tutorial.domain.Task; 5 | import com.shekhargulati.java8_tutorial.domain.TaskType; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.Files; 9 | import java.nio.file.Paths; 10 | import java.time.LocalDate; 11 | import java.time.Month; 12 | import java.util.List; 13 | import java.util.stream.IntStream; 14 | import java.util.stream.Stream; 15 | 16 | import static java.util.stream.Collectors.toList; 17 | 18 | 19 | public class DataUtils { 20 | 21 | public static Stream lines() { 22 | return filePathToStream("src/main/resources/book.txt"); 23 | } 24 | 25 | public static Stream negativeWords() { 26 | return filePathToStream("src/main/resources/negative-words.txt"); 27 | } 28 | 29 | public static Stream filePathToStream(String path) { 30 | try { 31 | return Files.lines(Paths.get("training", path)); 32 | } catch (IOException e) { 33 | throw new RuntimeException(e); 34 | } 35 | } 36 | 37 | public static IntStream range(int start, int end) { 38 | return IntStream.rangeClosed(start, end); 39 | } 40 | 41 | public static List getTasks() { 42 | Task task1 = new Task("Read Java 8 in action", TaskType.READING, LocalDate.of(2015, Month.SEPTEMBER, 20)).addTag("java").addTag("java8").addTag("books"); 43 | Task task2 = new Task("Write factorial program in Haskell", TaskType.CODING, LocalDate.of(2015, Month.SEPTEMBER, 20)).addTag("program").addTag("haskell").addTag("functional"); 44 | Task task3 = new Task("Read Effective Java", TaskType.READING, LocalDate.of(2015, Month.SEPTEMBER, 21)).addTag("java").addTag("books"); 45 | Task task4 = new Task("Write a blog on Stream API", TaskType.BLOGGING, LocalDate.of(2015, Month.SEPTEMBER, 21)).addTag("writing").addTag("stream").addTag("java8"); 46 | Task task5 = new Task("Write prime number program in Scala", TaskType.CODING, LocalDate.of(2015, Month.SEPTEMBER, 22)).addTag("scala").addTag("functional").addTag("program"); 47 | return Stream.of(task1, task2, task3, task4, task5).collect(toList()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /code/src/test/java/com/shekhargulati/java8_tutorial/ch01/CalculatorTest.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch01; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.CoreMatchers.equalTo; 6 | import static org.hamcrest.CoreMatchers.is; 7 | import static org.junit.Assert.assertThat; 8 | 9 | public class CalculatorTest { 10 | 11 | Calculator calculator = Calculator.getInstance(); 12 | 13 | @Test 14 | public void shouldAddTwoNumbers() throws Exception { 15 | int sum = calculator.add(1, 2); 16 | assertThat(sum, is(equalTo(3))); 17 | } 18 | 19 | @Test 20 | public void shouldSubtractTwoNumbers() throws Exception { 21 | int difference = calculator.subtract(3, 2); 22 | assertThat(difference, is(equalTo(1))); 23 | } 24 | 25 | @Test 26 | public void shouldDivideTwoNumbers() throws Exception { 27 | int quotient = calculator.divide(4, 2); 28 | assertThat(quotient, is(equalTo(2))); 29 | } 30 | 31 | @Test(expected = IllegalArgumentException.class) 32 | public void shouldThrowIllegalArgumentExceptionWhenDivisorIsZero() throws Exception { 33 | calculator.divide(4, 0); 34 | } 35 | 36 | @Test 37 | public void shouldMultiplyTwoNumbers() throws Exception { 38 | int product = calculator.multiply(3, 4); 39 | assertThat(product, is(equalTo(12))); 40 | } 41 | 42 | @Test 43 | public void shouldFindRemainderOfTwoNumbers() throws Exception { 44 | int remainder = calculator.remainder(100, 19); 45 | assertThat(remainder, is(equalTo(5))); 46 | 47 | } 48 | } -------------------------------------------------------------------------------- /code/src/test/java/com/shekhargulati/java8_tutorial/ch06/MapExampleTest.java: -------------------------------------------------------------------------------- 1 | package com.shekhargulati.java8_tutorial.ch06; 2 | 3 | import com.shekhargulati.java8_tutorial.domain.Task; 4 | import com.shekhargulati.java8_tutorial.domain.TaskType; 5 | import org.junit.Test; 6 | 7 | import java.util.Arrays; 8 | import java.util.LinkedHashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import static java.util.function.Function.identity; 13 | import static java.util.stream.Collectors.toMap; 14 | import static org.hamcrest.CoreMatchers.*; 15 | import static org.hamcrest.collection.IsMapContaining.hasEntry; 16 | import static org.junit.Assert.assertThat; 17 | 18 | public class MapExampleTest { 19 | 20 | @Test 21 | public void shouldCreateMapFromTaskList() throws Exception { 22 | Task t1 = new Task("Write blog on Java 8 Map improvements", TaskType.BLOGGING); 23 | Task t2 = new Task("Write factorial program in Java 8", TaskType.CODING); 24 | List tasks = Arrays.asList(t1, t2); 25 | 26 | Map taskIdToTaskMap = tasks.stream().collect(toMap(Task::getId, identity())); 27 | 28 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t1))); 29 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t2))); 30 | } 31 | 32 | @Test 33 | public void shouldCreateLinkedMapFromTaskList() throws Exception { 34 | Task t1 = new Task("Write blog on Java 8 Map improvements", TaskType.BLOGGING); 35 | Task t2 = new Task("Write factorial program in Java 8", TaskType.CODING); 36 | List tasks = Arrays.asList(t1, t2); 37 | 38 | Map taskIdToTaskMap = tasks.stream().collect(toMap(Task::getId, identity(), (k1, k2) -> k1, LinkedHashMap::new)); 39 | 40 | assertThat(taskIdToTaskMap, instanceOf(LinkedHashMap.class)); 41 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t1))); 42 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t2))); 43 | } 44 | 45 | @Test 46 | public void shouldHandleTaskListWithDuplicates() throws Exception { 47 | Task t1 = new Task("1", "Write blog on Java 8 Map improvements", TaskType.BLOGGING); 48 | Task t2 = new Task("1", "Write factorial program in Java 8", TaskType.CODING); 49 | List tasks = Arrays.asList(t1, t2); 50 | Map taskIdToTaskMap = tasks.stream().collect(toMap(Task::getId, identity(), (k1, k2) -> k2)); 51 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t2))); 52 | } 53 | } -------------------------------------------------------------------------------- /es/01-default-static-interface-methods.md: -------------------------------------------------------------------------------- 1 | Métodos Estáticos y por defecto en Interfaces 2 | -------- 3 | 4 | Todos entendemos que deberíamos programar con interfaces. Los interfaces dan al cliente un contrato que deberían usar sin preocuparse en los detalles de la implementación (p.e. las clases). Por lo tanto, fomentan **[el bajo acoplamiento](https://en.wikipedia.org/wiki/Loose_coupling)**. Diseñar interfaces limpios es uno de los aspectos más importantes en el diseño de APIs. Uno de los principios SOLID **[Segregación de interfaces](https://en.wikipedia.org/wiki/Interface_segregation_principle)** habla sobre diseñar interfaces específicos para el cliente más pequeños en vez de un interfaz más genérico. El diseño de interfaces es la clave para tener APIs limpios y efectivos para nuestas librerías y aplicaciones. 5 | 6 | > El código de esta sección está en [ch01 package](https://github.com/shekhargulati/java8-the-missing-tutorial/tree/master/code/src/main/java/com/shekhargulati/java8_tutorial/ch01). 7 | 8 | Si has diseñado algún API, con el tiempo, habrás sentido la necesidad de añadirle nuevos métodos. Una vez que se publica el API se hace imposible añadir métodos a un interfaz sin romper las implementaciones existentes. Para aclarar este punto vamos a suponer que estamos desarrollando un API sencillo de una calculadora `Calculator` que soporta las operaciones de sumar `add`, restar `subtract`, dividir `divide` y multiplicar `multiply`. Podemos escribir el interfaz `Calculator` como se muestra debajo. ***Para hacerlo sencillo usaremos enteros.*** 9 | 10 | ```java 11 | public interface Calculator { 12 | 13 | int add(int first, int second); 14 | 15 | int subtract(int first, int second); 16 | 17 | int divide(int number, int divisor); 18 | 19 | int multiply(int first, int second); 20 | } 21 | ``` 22 | 23 | Para respaldar este interfaz `Calculator` se desarrolló la implementación de `BasicCalculator` como se muestra abajo. 24 | 25 | ```java 26 | public class BasicCalculator implements Calculator { 27 | 28 | @Override 29 | public int add(int first, int second) { 30 | return first + second; 31 | } 32 | 33 | @Override 34 | public int subtract(int first, int second) { 35 | return first - second; 36 | } 37 | 38 | @Override 39 | public int divide(int number, int divisor) { 40 | if (divisor == 0) { 41 | throw new IllegalArgumentException("El divisor no puede ser cero."); 42 | } 43 | return number / divisor; 44 | } 45 | 46 | @Override 47 | public int multiply(int first, int second) { 48 | return first * second; 49 | } 50 | } 51 | ``` 52 | 53 | ## Métodos de Factoría Estáticos 54 | 55 | El API calculadora resultó ser muy útil y fácil de usar. Los usuarios sólo tienen que crear una instancia de `BasicCalculator` y ya pueden usar el API. Basta con usar código como el que se muestra debajo. 56 | 57 | ```java 58 | Calculator calculator = new BasicCalculator(); 59 | int sum = calculator.add(1, 2); 60 | 61 | BasicCalculator cal = new BasicCalculator(); 62 | int difference = cal.subtract(3, 2); 63 | ``` 64 | 65 | ¡Vaya! Los usuarios del API están usando la implementación del API en vez de su interfaz `Calculator`. Como la clase `BasicCalculator` era pública, tu API no obligaba a los usuarios a usar los interfaces. Si haces tu paquete `BasicCalculator` protegido tendrías que ofrecer una clase factoría estática que se dedicará a proveer la implementación de `Calculator`. Vamos a mejorar el código para manejar esto. 66 | 67 | Primero, haremos el paquete `BasicCalculator` protegido así los usuarios no podrán acceder a la clase directamente. 68 | 69 | ```java 70 | class BasicCalculator implements Calculator { 71 | // El resto permanece igual 72 | } 73 | ``` 74 | 75 | Luego, escribiremos una clase factoría que nos facilite la instancia de `Calculator` como se muestra debajo. 76 | 77 | ```java 78 | public abstract class CalculatorFactory { 79 | 80 | public static Calculator getInstance() { 81 | return new BasicCalculator(); 82 | } 83 | } 84 | ``` 85 | 86 | Ahora, los usuarios se verán obligados a usar el interfaz `Calculator` y no tendrán acceso a los detalles de la implementación. 87 | 88 | Aunque hemos logrado nuestra meta hemos aumentado el tamaño de nuestra API añadiendo una nueva clase `CalculatorFactory`. Ahora los usuarios del API tendrán que aprender una clase más antes de usar eficazmente nuestro API. Esta era la única solución disponible antes de Java 8. 89 | 90 | **Java 8 te permite declarar métodos estáticos dentro de un interfaz**. Esto permitirá a los diseñadores de APIs definir métodos de utilidad estáticos como `getInstance` en el propio interfaz y, por lo tanto, mantener el API sencillo y corto. Los métodos estáticos dentro de un interfaz se podrían usar para sustituir las clases de asistencia helpers estáticas (`CalculatorFactory`) que normalmente creamos para definir métodos de ayuda asociados a un tipo. Por ejemplo, la clase `Collections` es una clase de ayuda que define varios métodos de asistencia para trabajar con colecciones e interfaces asociadas. Los métodos definidos en la clase `Collections` podrían ser añadidos facilmente a `Collection` o a cualquiera de sus interfaces hijos. 91 | 92 | El código de abajo se puede mejorar en Java 8 añadiendo un método estático `getInstance` en el propio interfaz `Calculator`. 93 | 94 | ```java 95 | public interface Calculator { 96 | 97 | static Calculator getInstance() { 98 | return new BasicCalculator(); 99 | } 100 | 101 | int add(int first, int second); 102 | 103 | int subtract(int first, int second); 104 | 105 | int divide(int number, int divisor); 106 | 107 | int multiply(int first, int second); 108 | 109 | } 110 | ``` 111 | 112 | ## La Evolución del API con el tiempo 113 | 114 | Algunos de los consumidores decidieron o bien, ampliar el API `Calculator` añadiendo métodos como `remainder`, o escribir su propia implementación del interfaz `Calculator`. Tras hablar con tus usuarios sacaste la conclusión de que a la mayoría de ellos les gustaría tener un método `remainder` en el interfaz `Calculator`. Parecía un cambio muy simple al API por lo que añadiste un método nuevo. 115 | 116 | ```java 117 | public interface Calculator { 118 | 119 | static Calculator getInstance() { 120 | return new BasicCalculator(); 121 | } 122 | 123 | int add(int first, int second); 124 | 125 | int subtract(int first, int second); 126 | 127 | int divide(int number, int divisor); 128 | 129 | int multiply(int first, int second); 130 | 131 | int remainder(int number, int divisor); // Nuevo método añadido al API 132 | } 133 | ``` 134 | 135 | Añadir un método rompió la compatibilidad origen del API. Esto quiere decir que los usuarios que implementaron el interfaz `Calculator` tendrían que añadir el método `remainder` o su código no compilará. Este es un grave problema para los diseñadores de APIs ya que hace que la evolución de los mismos sea complicada. Antes de Java 8, no era posible tener implementación de métodos en los interfaces, lo que es un problema cuando se requiere ampliar un API, p.e. añadiendo uno o más métodos a la definición de la interfaz. 136 | 137 | Para permitir a los APIs evolucionar con el tiempo, Java 9 permite a los usuarios proporcionar implementaciones por defecto a métodos definidos en el interfaz. Estos se llaman métodos por defecto **default** o de defensa **defender**. La clase que implementa el interfaz no necesita proporcionar implementación para estos métodos. Si la clase que implementa el interfaz proporciona la implementación del método entonces se usará esta en vez de la implementación por defecto del interfaz. El interfaz `List` tiene definidos algunos métodos por defecto como `replaceAll`, `sort` y `splitIterator`. 138 | 139 | ```java 140 | default void replaceAll(UnaryOperator operator) { 141 | Objects.requireNonNull(operator); 142 | final ListIterator li = this.listIterator(); 143 | while (li.hasNext()) { 144 | li.set(operator.apply(li.next())); 145 | } 146 | } 147 | ``` 148 | 149 | Podemos resolver nuestro problema del API definiendo un método por defecto como se muestra abajo. Los métodos por defecto se definen normalmente usando métodos ya existentes -- `remainder` se define usando los métodos `subtract`, `multiply` y `divide`. 150 | 151 | ```java 152 | default int remainder(int number, int divisor) { 153 | return subtract(number, multiply(divisor, divide(number, divisor))); 154 | } 155 | ``` 156 | 157 | ## Herencia múltiple 158 | 159 | Una clase puede extender sólo una clase pero puede implementar múltiples interfaces. Ahora que es posible tener implementación de métodos en interfaces Java conseguimos herencia múltiple de comportamiento. Java ya tenía herencia múltiple a nivel de tipo con las interfaces y ahora también a nivel de comportamiento. 160 | 161 | Existen tres reglas de resolución que ayudan a decidir que método será elegido: 162 | 163 | **Regla 1: Los métodos declarados en las clases tendrán preferencia sobre los definidos en las interfaces.** 164 | 165 | ```java 166 | interface A { 167 | default void doSth(){ 168 | System.out.println("Dentro de A"); 169 | } 170 | } 171 | 172 | class App implements A { 173 | 174 | @Override 175 | public void doSth() { 176 | System.out.println("Dentro de App"); 177 | } 178 | 179 | public static void main(String[] args) { 180 | new App().doSth(); 181 | } 182 | } 183 | ``` 184 | 185 | Esto imprimirá `Dento de App` ya que los métodos declarados en la clase tienen prioridad sobre los métodos declarados en el interfaz. 186 | 187 | **Regla 2: En otro caso, se eligirá el interfaz más específico** 188 | 189 | ```java 190 | interface A { 191 | default void doSth() { 192 | System.out.println("Dentro de A"); 193 | } 194 | } 195 | 196 | interface B {} 197 | 198 | interface C extends A { 199 | default void doSth() { 200 | System.out.println("Dentro de C"); 201 | } 202 | } 203 | 204 | class App implements C, B, A { 205 | 206 | public static void main(String[] args) { 207 | new App().doSth(); 208 | } 209 | } 210 | ``` 211 | 212 | Esto imprimirá `Dentro de C`. 213 | 214 | **Regla 3: Si no, la clase tiene que llamar explícitamente a la implementación que desea** 215 | 216 | ```java 217 | interface A { 218 | default void doSth() { 219 | System.out.println("Dentro de A"); 220 | } 221 | } 222 | 223 | interface B { 224 | default void doSth() { 225 | System.out.println("Dentro de B"); 226 | } 227 | } 228 | 229 | class App implements B, A { 230 | 231 | @Override 232 | public void doSth() { 233 | B.super.doSth(); 234 | } 235 | 236 | public static void main(String[] args) { 237 | new App().doSth(); 238 | } 239 | } 240 | ``` 241 | Esto imprimirá `Dentro de B`. 242 | 243 | [![Analytics](https://ga-beacon.appspot.com/UA-74043032-1/malobato/java8-the-missing-tutorial/01-default-static-interface-methods)](https://github.com/igrigorik/ga-beacon) 244 | -------------------------------------------------------------------------------- /es/04-collectors.md: -------------------------------------------------------------------------------- 1 | Collectors 2 | ------ 3 | 4 | En el [capítulo 2](https://github.com/malobato/java8-the-missing-tutorial/blob/master/03-streams.md), aprendiste que el API Stream puede ayudarte a trabajar con colecciones de manera declarativa. Observamos el método `collect()`, que es una operación terminal que acumula el conjunto de resultados de una tubería de flujo en un `List`; es una operación de reducción que reduce un flujo a un valor. El valor podría ser un `Collection`, un `Map` o un objeto valor. Puedes usar `collect()` para obtener lo siguiente: 5 | 6 | 1. **Reducir un flujo a un simple valor:** El resultado de la ejecución del flujo se puede reducir a un simple valor. Un valor simple podría ser una `Collection` o un valor numérico como un *int*, *double*, etc o un objeto de valor personalizado. 7 | 8 | 2. **Agrupar elementos en un flujo:** Agrupar todas las tareas de un flujo por tipo de tarea. El resultado será un `Map>` con cada entrada conteniendo el tipo de tarea y sus tareas asociadas. También puedes usar cualquier otra colección en vez de un `List`. Si no necesitas todas las tareas asociadas con un tipo de tarea, también puedes producir `Map`. Un ejemplo podría ser agrupar las tareas por tipo y obtener la primera tarea creada. 9 | 10 | 3. **Dividir los elementos de un flujo:** Puedes dividir un flujo en dos grupos: tareas esperadas y completadas. 11 | 12 | ## Collector en Acción 13 | 14 | Para demostrar el poder de `Collector` vamos a observar el ejemplo donde tenemos que agrupar tareas por su tipo. En Java 8, podemos conseguir agrupar por tipo de tarea con el siguiente código. **(Revisar el [capítulo 2](https://github.com/malobato/java8-the-missing-tutorial/blob/master/02-lambdas.md) donde hablamos sobre el ejemplo que seguimos en esta serie).** 15 | 16 | ```java 17 | private static Map> groupTasksByType(List tasks) { 18 | return tasks.stream().collect(Collectors.groupingBy(task -> task.getType())); 19 | } 20 | ``` 21 | 22 | El código siguiente usa el método `groupingBy()` de `Collector` definido en la clase de utilidad `Collectors`. Crea un mapa con clave `TaskType` y valor una lista que contiene todas las tareas con el mismo `TaskType`. Para conseguir lo mismo en Java 7 habría que escribir el siguiente código: 23 | 24 | ```java 25 | public static void main(String[] args) { 26 | List tasks = getTasks(); 27 | Map> allTasksByType = new HashMap<>(); 28 | for (Task task : tasks) { 29 | List existingTasksByType = allTasksByType.get(task.getType()); 30 | if (existingTasksByType == null) { 31 | List tasksByType = new ArrayList<>(); 32 | tasksByType.add(task); 33 | allTasksByType.put(task.getType(), tasksByType); 34 | } else { 35 | existingTasksByType.add(task); 36 | } 37 | } 38 | for (Map.Entry> entry : allTasksByType.entrySet()) { 39 | System.out.println(String.format("%s =>> %s", entry.getKey(), entry.getValue())); 40 | } 41 | } 42 | ``` 43 | 44 | ## Collectors: Operaciones de reducción comunes 45 | 46 | La clase utilidad `Collectors` proporciona un montón de métodos estáticos de utilidad para crear **acumuladores** para la mayoría de casos de uso comunes como acumular elementos en una colección, agrupar y particionar elementos, y resumir elementos de acuerdo a varios criterios. Cubriremos la mayoría de casos comunes de `Collector` en este blog. 47 | 48 | 49 | ## Reducir a un simple valor 50 | 51 | Como ya vimos, los **acumuladores** se pueden usar para recoger la salida de un flujo en una colección o generar un valor simple. 52 | 53 | ### Recoger datos en una lista 54 | 55 | Vamos a escribir nuestro primer caso de prueba: dada una lista de tareas queremos recoger sus títulos en una lista. 56 | 57 | ```java 58 | import static java.util.stream.Collectors.toList; 59 | 60 | public class Example2_ReduceValue { 61 | public List allTitles(List tasks) { 62 | return tasks.stream().map(Task::getTitle).collect(toList()); 63 | } 64 | } 65 | ``` 66 | 67 | El acumulador `toList()` usa el método `add()` de `List` para añadir elementos dentro de la lista resultante. Usa un `ArrayList` como implementación de la lista. 68 | 69 | ### Recoger datos en un conjunto 70 | 71 | Si queremos estar seguros de que sólo recogemos títulos únicos y no nos preocupa el orden, podemos usar el acumulador `toSet()`. 72 | 73 | ```java 74 | import static java.util.stream.Collectors.toSet; 75 | 76 | public Set uniqueTitles(List tasks) { 77 | return tasks.stream().map(Task::getTitle).collect(toSet()); 78 | } 79 | ``` 80 | 81 | El método `toSet()` usa un `HashSet` como implementación del conjunto para guardar el resultado. 82 | 83 | ### Recoger datos en un mapa 84 | 85 | Puedes convertir un flujo en un mapa usando el acumulador `toMap()`. El acumulador `toMap()` toma como parámetros dos funciones de mapeado para extraer la clave y los valores del mapa. En el código siguiente `Task::getTitle` es una función que toma una tarea y produce una clave con un único título. **task -> task** es una expresión lambda que se devuelve a si misma. P. ej. la tarea en este caso. 86 | 87 | ```java 88 | private static Map taskMap(List tasks) { 89 | return tasks.stream().collect(toMap(Task::getTitle, task -> task)); 90 | } 91 | ``` 92 | 93 | Podemos mejorar el código anterior usando el método por defecto `identity()` en la interfaz `Function` para generar código más limpio y que transmita mucho mejor la intención del programador. 94 | 95 | ```java 96 | import static java.util.function.Function.identity; 97 | 98 | private static Map taskMap(List tasks) { 99 | return tasks.stream().collect(toMap(Task::getTitle, identity())); 100 | } 101 | ``` 102 | 103 | El código para crear un mapa a partir de un flujo lanzará una excepción si existen claves duplicadas. Obtendrás un error como el siguiente. 104 | 105 | ``` 106 | Exception in thread "main" java.lang.IllegalStateException: Duplicate key Task{title='Read Version Control with Git book', type=READING} 107 | at java.util.stream.Collectors.lambda$throwingMerger$105(Collectors.java:133) 108 | ``` 109 | 110 | Puedes controlar los duplicados usando otra variante de la función `toMap()` que nos permite especificar una función de unión. La función de unión permite al cliente especificar como quiere resolver la colisión entre valores asociados a la misma clave. En el código siguiente, sólo nos quedamos con el último valor, pero puedes escribir un algoritmo para resolver la colisión. 111 | 112 | ```java 113 | private static Map taskMap_duplicates(List tasks) { 114 | return tasks.stream().collect(toMap(Task::getTitle, identity(), (t1, t2) -> t2)); 115 | } 116 | ``` 117 | 118 | Puedes usar cualquier otra implementación de mapa usando la tercera variante del método `toMap()`. Esto require que especifiques el `Map` `Supplier` que se usará para guardar el resultado. 119 | 120 | ``` 121 | public Map collectToMap(List tasks) { 122 | return tasks.stream().collect(toMap(Task::getTitle, identity(), (t1, t2) -> t2, LinkedHashMap::new)); 123 | } 124 | ``` 125 | 126 | Similar al acumulador `toMap()` también está el acumulador `toConcurrentMap()` que produce un `ConcurrentMap` en vez de un `HashMap`. 127 | 128 | ### Usando otras colecciones 129 | 130 | Los acumuladores específicos como `toList()` y `toSet()` no te permiten especificar la implementación de la lista o del conjunto. Puedes usar el acumulador `toCollection()` cuando quieras recoger el resultado en otros tipos de colecciones como se muestra a continuación. 131 | 132 | ``` 133 | private static LinkedHashSet collectToLinkedHaskSet(List tasks) { 134 | return tasks.stream().collect(toCollection(LinkedHashSet::new)); 135 | } 136 | ``` 137 | 138 | ### Encontrando la tarea con el título más largo 139 | 140 | ```java 141 | public Task taskWithLongestTitle(List tasks) { 142 | return tasks.stream().collect(collectingAndThen(maxBy((t1, t2) -> t1.getTitle().length() - t2.getTitle().length()), Optional::get)); 143 | } 144 | ``` 145 | 146 | ### Contando el número total de etiquetas 147 | 148 | ```java 149 | public int totalTagCount(List tasks) { 150 | return tasks.stream().collect(summingInt(task -> task.getTags().size())); 151 | } 152 | ``` 153 | 154 | ### Generando un resumen de títulos de tarea 155 | 156 | ```java 157 | public String titleSummary(List tasks) { 158 | return tasks.stream().map(Task::getTitle).collect(joining(";")); 159 | } 160 | ``` 161 | 162 | ## Agrupando acumuladores 163 | 164 | Uno de los casos de uso más comunes es agrupar elementos. Vamos a ver varios ejemplos para comprender como podemos realizar agrupaciones. 165 | 166 | ### Ejemplo 1: Agrupando tareas por tipo 167 | 168 | Vamos a ver el ejemplo mostrado abajo donde queremos agrupar todas las tareas basándonos en su `TaskType`. Puedes realizar esta tarea de una forma muy sencilla usando el acumulador `groupingBy()` de la clase de utilidad `Collectors`. Puedes acortarlo más usando referencias a método e importaciones estáticas. 169 | 170 | ```java 171 | import static java.util.stream.Collectors.groupingBy; 172 | 173 | private static Map> groupTasksByType(List tasks) { 174 | return tasks.stream().collect(groupingBy(Task::getType)); 175 | } 176 | ``` 177 | 178 | Producirá la siguiente salida. 179 | 180 | ``` 181 | {CODING=[Task{title='Write a mobile application to store my tasks', type=CODING, createdOn=2015-07-03}], WRITING=[Task{title='Write a blog on Java 8 Streams', type=WRITING, createdOn=2015-07-04}], READING=[Task{title='Read Version Control with Git book', type=READING, createdOn=2015-07-01}, Task{title='Read Java 8 Lambdas book', type=READING, createdOn=2015-07-02}, Task{title='Read Domain Driven Design book', type=READING, createdOn=2015-07-05}]} 182 | ``` 183 | 184 | ### Ejemplo 2: Agrupando por etiquetas 185 | 186 | ```java 187 | private static Map> groupingByTag(List tasks) { 188 | return tasks.stream(). 189 | flatMap(task -> task.getTags().stream().map(tag -> new TaskTag(tag, task))). 190 | collect(groupingBy(TaskTag::getTag, mapping(TaskTag::getTask,toList()))); 191 | } 192 | 193 | private static class TaskTag { 194 | final String tag; 195 | final Task task; 196 | 197 | public TaskTag(String tag, Task task) { 198 | this.tag = tag; 199 | this.task = task; 200 | } 201 | 202 | public String getTag() { 203 | return tag; 204 | } 205 | 206 | public Task getTask() { 207 | return task; 208 | } 209 | } 210 | ``` 211 | 212 | ### Ejemplo 3: Agrupando tareas por etiqueta y número 213 | 214 | Combinando clasificadores y acumuladores. 215 | 216 | ```java 217 | private static Map tagsAndCount(List tasks) { 218 | return tasks.stream(). 219 | flatMap(task -> task.getTags().stream().map(tag -> new TaskTag(tag, task))). 220 | collect(groupingBy(TaskTag::getTag, counting())); 221 | } 222 | ``` 223 | 224 | ### Ejemplo 4: Agrupando por tipo de tarea y fecha de creación 225 | 226 | ```java 227 | private static Map>> groupTasksByTypeAndCreationDate(List tasks) { 228 | return tasks.stream().collect(groupingBy(Task::getType, groupingBy(Task::getCreatedOn))); 229 | } 230 | ``` 231 | 232 | ## Dividiendo en partes 233 | 234 | Hay veces en las que quieres partir un conjunto de datos en dos basándote en un predicado. Por ejemplo, podemos partir tareas en dos grupos definiendo una función de reparto que parta las tareas en dos grupos: uno con fecha de vencimiento anterior a hoy y otro con fecha de vencimiento posterior a hoy. 235 | 236 | ```java 237 | private static Map> partitionOldAndFutureTasks(List tasks) { 238 | return tasks.stream().collect(partitioningBy(task -> task.getDueOn().isAfter(LocalDate.now()))); 239 | } 240 | ``` 241 | 242 | ## Generando estadísticas 243 | 244 | Otro grupo de acumuladores que son muy útiles son los acumuladores que producen estadísticas. Estos trabajan con tipos de datos elementales como int, double, long y pueden usarse para generar estadísticas como la que se muestra a continuación. 245 | 246 | ```java 247 | IntSummaryStatistics summaryStatistics = tasks.stream().map(Task::getTitle).collect(summarizingInt(String::length)); 248 | System.out.println(summaryStatistics.getAverage()); //32.4 249 | System.out.println(summaryStatistics.getCount()); //5 250 | System.out.println(summaryStatistics.getMax()); //44 251 | System.out.println(summaryStatistics.getMin()); //24 252 | System.out.println(summaryStatistics.getSum()); //162 253 | ``` 254 | 255 | También existen otras variantes para otros tipos elementales como `LongSummaryStatistics` y `DoubleSummaryStatistics`. 256 | 257 | También puedes combinar un `IntSummaryStatistics` con otro usando la operación `combine`. 258 | 259 | ```java 260 | firstSummaryStatistics.combine(secondSummaryStatistics); 261 | System.out.println(firstSummaryStatistics) 262 | ``` 263 | 264 | ## Uniendo todos los títulos 265 | 266 | ```java 267 | private static String allTitles(List tasks) { 268 | return tasks.stream().map(Task::getTitle).collect(joining(", ")); 269 | } 270 | ``` 271 | 272 | ## Escribiendo un acumulador personalizado 273 | 274 | ```java 275 | import com.google.common.collect.HashMultiset; 276 | import com.google.common.collect.Multiset; 277 | 278 | import java.util.Collections; 279 | import java.util.EnumSet; 280 | import java.util.Set; 281 | import java.util.function.BiConsumer; 282 | import java.util.function.BinaryOperator; 283 | import java.util.function.Function; 284 | import java.util.function.Supplier; 285 | import java.util.stream.Collector; 286 | 287 | public class MultisetCollector implements Collector, Multiset> { 288 | 289 | @Override 290 | public Supplier> supplier() { 291 | return HashMultiset::create; 292 | } 293 | 294 | @Override 295 | public BiConsumer, T> accumulator() { 296 | return (set, e) -> set.add(e, 1); 297 | } 298 | 299 | @Override 300 | public BinaryOperator> combiner() { 301 | return (set1, set2) -> { 302 | set1.addAll(set2); 303 | return set1; 304 | }; 305 | } 306 | 307 | @Override 308 | public Function, Multiset> finisher() { 309 | return Function.identity(); 310 | } 311 | 312 | @Override 313 | public Set characteristics() { 314 | return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH)); 315 | } 316 | } 317 | ``` 318 | 319 | ```java 320 | import com.google.common.collect.Multiset; 321 | 322 | import java.util.Arrays; 323 | import java.util.List; 324 | 325 | public class MultisetCollectorExample { 326 | 327 | public static void main(String[] args) { 328 | List names = Arrays.asList("shekhar", "rahul", "shekhar"); 329 | Multiset set = names.stream().collect(new MultisetCollector<>()); 330 | 331 | set.forEach(str -> System.out.println(str + ":" + set.count(str))); 332 | 333 | } 334 | } 335 | ``` 336 | 337 | ## Contador de palabras en Java 8 338 | 339 | Terminaremos esta sección escribiendo en Java 8 el famoso ejemplo de contar palabras usando flujos y acumuladores. 340 | 341 | ```java 342 | public static void wordCount(Path path) throws IOException { 343 | Map wordCount = Files.lines(path) 344 | .parallel() 345 | .flatMap(line -> Arrays.stream(line.trim().split("\\s"))) 346 | .map(word -> word.replaceAll("[^a-zA-Z]", "").toLowerCase().trim()) 347 | .filter(word -> word.length() > 0) 348 | .map(word -> new SimpleEntry<>(word, 1)) 349 | .collect(groupingBy(SimpleEntry::getKey, counting())); 350 | wordCount.forEach((k, v) -> System.out.println(String.format("%s ==>> %d", k, v))); 351 | } 352 | ``` 353 | 354 | [![Analytics](https://ga-beacon.appspot.com/UA-74043032-1/malobato/java8-the-missing-tutorial/04-collectors)](https://github.com/igrigorik/ga-beacon) 355 | -------------------------------------------------------------------------------- /es/05-optionals.md: -------------------------------------------------------------------------------- 1 | Optionals 2 | ---- 3 | 4 | Cada programador de Java, ya sea principiante, novato o experto, ha experimentado en su vida un `NullPointerException`. Es un hecho que ningún programador de Java puede negar. Todos hemos malgastado o perdido muchas horas tratando de corregir errores debidos a un `NullPointerException`. De acuerdo al JavaDoc de `NullPointerException`, ***Una excepción NullPointerException se arroja cuando una aplicación intenta usar un nulo en vez de un objeto.*** Esto quiere decir que si invocamos un método o intentamos acceder a una propiedad sobre una referencia ***nula***, nuestro código reventará y se lanzará un `NullPointerException`. En este capítulo, aprenderás como escribir código libre de nulos usando el `Optional` de Java 8. 5 | 6 | > Como comentario, si miras en el JavaDoc de `NullPointerException` encontrarás que el autor de esta excepción está ***sin atribuir***. Si se usa, el autor es desconocido o está sin atribuir, quiere decir que nadie quiere responsabilizarse del `NullPointerException` ;). 7 | 8 | 9 | ## ¿Qué son las referencias nulas? 10 | 11 | En el 2009 en la conferencia QCon ***[Sir Tony Hoare](https://en.wikipedia.org/wiki/Tony_Hoare)*** 12 | declaró que él inventó el tipo de referencia nulo mientras diseñaba el lenguaje de programación ***ALGOL W***. El nulo fue diseñado para indicar la ausencia de un valor. Designó a las *referencias nulas* como *el error del billón de dolares*. Puedes ver el video completo de su presentación en [Infoq](http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare). 13 | 14 | La mayoría de los lenguajes de programación, como C, C++, C#, Java, Scala, etc, tienen tipos nulos como parte de su sistema de tipado lo que permite establecer como valor un **Nulo** en vez de otros posibles valores de tipos de datos. 15 | 16 | ## ¿Por qué las referencias nulas son malas? 17 | 18 | Vamos a ver el siguiente ejemplo sobre las clases del dominio de gestión de tareas. Nuestro modelo de dominio es muy sencillo, sólo tiene dos clases: Task y User. Una tarea se puede asignar a un usuario. 19 | 20 | > El código de esta sección está en el [paquete ch05](https://github.com/shekhargulati/java8-the-missing-tutorial/tree/master/code/src/main/java/com/shekhargulati/java8_tutorial/ch05). 21 | 22 | ```java 23 | public class Task { 24 | private final String id; 25 | private final String title; 26 | private final TaskType type; 27 | private User assignedTo; 28 | 29 | public Task(String id, String title, TaskType type) { 30 | this.id = id; 31 | this.title = title; 32 | this.type = type; 33 | } 34 | 35 | public Task(String title, TaskType type) { 36 | this.id = UUID.randomUUID().toString(); 37 | this.title = title; 38 | this.type = type; 39 | } 40 | 41 | public String getTitle() { 42 | return title; 43 | } 44 | 45 | public TaskType getType() { 46 | return type; 47 | } 48 | 49 | public User getAssignedTo() { 50 | return assignedTo; 51 | } 52 | 53 | public void setAssignedTo(User assignedTo) { 54 | this.assignedTo = assignedTo; 55 | } 56 | } 57 | 58 | public class User { 59 | 60 | private final String username; 61 | private String address; 62 | 63 | public User(String username) { 64 | this.username = username; 65 | } 66 | 67 | public String getUsername() { 68 | return username; 69 | } 70 | 71 | public String getAddress() { 72 | return address; 73 | } 74 | 75 | public void setAddress(String address) { 76 | this.address = address; 77 | } 78 | } 79 | ``` 80 | 81 | Dado el modelo de dominio anterior, si tenemos que encontrar al usuario asignado a una tarea con identificador `taskId` tendríamos que escribir el siguiente código. 82 | 83 | ```java 84 | public String taskAssignedTo(String taskId) { 85 | return taskRepository.find(taskId).getAssignedTo().getUsername(); 86 | } 87 | ``` 88 | 89 | El problema más grave del código mostrado es que la ausencia de un valor no es visible en el API. P. ej. si `task` no está asignada a algún usuario el código lanzará la excepción `NullPointerException` cuando se invoque a `getAssignedTo()`. Y `taskRepository.find(taskId)` también podría devolver un `null`. Esto fuerza a los clientes del API a programar de manera defensiva y comprobar los nulos como se muestra a continuación. 90 | 91 | ```java 92 | public String taskAssignedTo(String taskId) throws TaskNotFoundException { 93 | Task task = taskRepository.find(taskId); 94 | if (task != null) { 95 | User assignedTo = task.getAssignedTo(); 96 | if (assignedTo != null) 97 | return assignedTo.getUsername(); 98 | return "NotAssigned"; 99 | } 100 | throw new TaskNotFoundException(String.format("No task exist with id '%s'", taskId)); 101 | } 102 | ``` 103 | 104 | El código mostrado omite la intención del desarrollador y aumenta el código con comprobaciones `if-null`. El programador, de algún modo, quería usar tipos de datos opcionales pero se vió forzado a escribir comprobaciones `if-null`. Estoy seguro de que has escrito este tipo de código en tu día a día como programador. 105 | 106 | ## Patrón de Objeto Nulo 107 | 108 | Una solución típica para trabajar con referencias `null` es usar [el patrón de Objeto Nulo](https://en.wikipedia.org/wiki/Null_Object_pattern). La idea detrás de este patrón es muy simple, en vez de devolver un nulo, deberías de devolver un objeto nulo que implemente tu interfaz o tu clase. De este modo puedes crear un `NullUser` como se muestra a continuación. 109 | 110 | ```java 111 | public class NullUser extends User { 112 | 113 | public NullUser(String username) { 114 | super("NotAssigned"); 115 | } 116 | } 117 | ``` 118 | 119 | Ahora sí que podríamos devolver un `NullUser` cuando no haya asignado un usuario a una tarea. Podemos cambiar el método `getAssignedTo` para que devuelva un `NullUser` cuando no haya asignado un usuario a la tarea. 120 | 121 | ```java 122 | public User getAssignedTo() { 123 | return assignedTo == null ? NullUser.getInstance() : assignedTo; 124 | } 125 | ``` 126 | 127 | Ahora el código del cliente se puede simplificar para que no compruebe los nulos como sigue. En este ejemplo, no tiene sentido usar el patrón Objeto Nulo en `Task`, ya que la ausencia de la tarea en el repositorio sería una situación de excepción. Además, al añadir `TaskNotFoundException` en la sección de `throws`, hemos hecho explícito para el cliente que el código puede arrojar una excepción. 128 | 129 | ```java 130 | public String taskAssignedTo(String taskId) throws TaskNotFoundException { 131 | Task task = taskRepository.find(taskId); 132 | if (task != null) { 133 | return task.getAssignedTo().getUsername(); 134 | } 135 | throw new TaskNotFoundException(String.format("No task exist with id '%s'", taskId)); 136 | } 137 | ``` 138 | 139 | ## Java 8 -- Presentación del tipo de dato Optional 140 | 141 | Java 8 presenta un nuevo tipo de dato ***java.util.Optional*** que encapsula un valor vacío. Aclara la intención del API. Si una función devuelve un valor de tipo Optional le dice al cliente que podría no devolver un valor. Cuando usas el tipo `Optional`, como desarrollador, haces visible a través del sistema de tipos que el valor puede no estar presente y el cliente puede trabajar con él limpiamente. El propósito de usar el tipo `Optional` es ayudar a los diseñadores de APIs a hacer visible a sus clientes de forma explícita si deberían de esperar un valor opcional o no mirando la firma del método. 142 | 143 | Vamos a actualizar nuestro modelo de dominio para reflejar valores opcionales. 144 | 145 | ```java 146 | import java.util.Optional; 147 | 148 | public class Task { 149 | private final String title; 150 | private final Optional assignedTo; 151 | private final String id; 152 | 153 | public Task(String id, String title) { 154 | this.id = id; 155 | this.title = title; 156 | assignedTo = Optional.empty(); 157 | } 158 | 159 | public Task(String id, String title, User assignedTo) { 160 | this.id = id; 161 | this.title = title; 162 | this.assignedTo = Optional.ofNullable(assignedTo); 163 | } 164 | 165 | public String getId() { 166 | return id; 167 | } 168 | 169 | public String getTitle() { 170 | return title; 171 | } 172 | 173 | public Optional getAssignedTo() { 174 | return assignedTo; 175 | } 176 | } 177 | 178 | import java.util.Optional; 179 | 180 | public class User { 181 | 182 | private final String username; 183 | private final Optional address; 184 | 185 | public User(String username) { 186 | this.username = username; 187 | this.address = Optional.empty(); 188 | } 189 | 190 | public User(String username, String address) { 191 | this.username = username; 192 | this.address = Optional.ofNullable(address); 193 | } 194 | 195 | public String getUsername() { 196 | return username; 197 | } 198 | 199 | public Optional getAddress() { 200 | return address; 201 | } 202 | } 203 | ``` 204 | 205 | El uso del tipo de dato `Optional` en el modelo de datos hace explícito que `Task` referencia a un ***Optional*** y también que `User` tiene una dirección **Optional**. Ahora quien quiera tratar de trabajar con `assignedTo()` debería saber que podría no estar presente y lo podría controlar de manera declarativa. Hablaremos de los métodos `Optional.empty()` y `Optional.of()` en la próxima sección. 206 | 207 | ## Trabajando con métodos de creación en el API java.util.Optional 208 | 209 | En el modelo de dominio mostrado arriba, usamos un par de métodos de creación de la clase `Optional` pero no hablé sobre ellos. Ahora vamos a hablar sobre tres métodos de creación que forman parte del API `Optional`. 210 | 211 | * **Optional.empty()**: Se usa para crear un `Optional` cuando no existe un valor, como hicimos arriba en el constructor `this.assignedTo = Optional.empty();`. 212 | 213 | * **Optional.of(T value)**: Se usa para crear un `Optional` a partir de un valor no nulo. Lanza una excepción `NullPointerException` si el valor es nulo. Lo usamos anteriormente en `this.address = Optional.of(address);`. 214 | 215 | * **Optional.ofNullable(T value)**: Este método estático de factoría funciona tanto para valores nulos como no nulos. Para valores nulos creará un `Optional` vacío y para valores no nulos creará un `Optional` con el valor. 216 | 217 | Abajo mostramos en ejemplo sencillo de como puedes escribir un API usando `Optional`. 218 | 219 | ```java 220 | public class TaskRepository { 221 | 222 | private static final Map TASK_STORE = new ConcurrentHashMap<>(); 223 | 224 | public Optional find(String taskId) { 225 | return Optional.ofNullable(TASK_STORE.get(taskId)); 226 | } 227 | 228 | public void add(Task task) { 229 | TASK_STORE.put(task.getId(), task); 230 | } 231 | } 232 | ``` 233 | 234 | ## Usando valores Optional 235 | 236 | Se puede pensar en `Optional` como un flujo de un único elemento. Tiene métodos similares al API `Stream` como map, filter, flatMap que se pueden usar para trabajar con los valores contenidos en el `Optional`. 237 | 238 | ### Obtener el título de una tarea 239 | 240 | Para leer el valor del título de una tarea escribiríamos el siguiente código. La función `map()` se usa para transformar de `Optional` a `Optional`. El método `orElseThrow()` se usa para lanzar una excepción personalizada de negocio cuando no se encuentra la tarea. 241 | 242 | ```java 243 | public String taskTitle(String taskId) { 244 | return taskRepository. 245 | find(taskId). 246 | map(Task::getTitle). 247 | orElseThrow(() -> new TaskNotFoundException(String.format("No task exist for id '%s'",taskId))); 248 | } 249 | ``` 250 | 251 | Existen tres variantes del método `orElse()`: 252 | 253 | 1. **orElse(T t)**: Se usa para devolver un valor cuando exista o el valor que se le pasa como parámetro. P. ej. `Optional.ofNullable(null).orElse("NoValue")` devolverá `NoValue` ya que no existe el valor. 254 | 255 | 2. **orElseGet**: Devolverá el valor si está presente, y si no generará un nuevo valor resultado de invocar el método `get()` de `Supplier`. Por ejemplo, `Optional.ofNullable(null).orElseGet(() -> UUID.randomUUID().toString()` se usaría para generar valores de forma perezosa cuando no exista un valor. 256 | 257 | 3. **orElseThrow**: Esto permite a los clientes lanzar sus propias excepciones personalizadas cuando no exista un valor. 258 | 259 | El método `find()` mostrado en el ejemplo de antes devuelve un `Optional` que el cliente puede usar para obtener el valor. Supón que queremos obtener el título de una tarea a partir del `Optional`, podemos hacerlo usando la función `map()` como se muestra a continuación. 260 | 261 | ### Obtener el nombre del usuario asignado 262 | 263 | Para obtener el nombre del usuario que está asignado a una tarea podemos usar el método `flatMap()` de la siguiente manera. 264 | 265 | ```java 266 | public String taskAssignedTo(String taskId) { 267 | return taskRepository. 268 | find(taskId). 269 | flatMap(task -> task.getAssignedTo().map(user -> user.getUsername())). 270 | orElse("NotAssigned"); 271 | } 272 | ``` 273 | 274 | ### Filtrar con Optional 275 | 276 | La tercera operación, como la del API de `Stream`, soportada por `Optional` es `filter`, que te permite filtrar un `Optional` por una propiedad como se muestra en el siguiente ejemplo. 277 | 278 | ```java 279 | public boolean isTaskDueToday(Optional task) { 280 | return task.flatMap(Task::getDueOn).filter(d -> d.isEqual(LocalDate.now())).isPresent(); 281 | } 282 | ``` 283 | [![Analytics](https://ga-beacon.appspot.com/UA-74043032-1/malobato/java8-the-missing-tutorial/05-optionals)](https://github.com/igrigorik/ga-beacon) 284 | -------------------------------------------------------------------------------- /es/06-map.md: -------------------------------------------------------------------------------- 1 | Mejoras en Map 2 | --------- 3 | 4 | `Map` es una de las estructuras de datos más importantes. En Java 8, se han añadido un montón de mejoras al API Map que harán más fácil el trabajar con él. Veremos todas estas mejoras una a una. Cada característica se mostrará con su correspondiente caso de prueba JUnit. 5 | 6 | ## Crear un Map a partir de un List 7 | 8 | La mayoría de las veces queremos crear un mapa a partir de datos ya existentes. Vamos a suponer que tenemos una lista de tareas; cada tarea tiene un identificador y otros datos asociados como el título, la descripción, etc. 9 | 10 | ```java 11 | import static java.util.function.Function.identity; 12 | import static java.util.stream.Collectors.toMap; 13 | 14 | @Test 15 | public void shouldCreateMapFromTaskList() throws Exception { 16 | Task t1 = new Task("Write blog on Java 8 Map improvements", TaskType.BLOGGING); 17 | Task t2 = new Task("Write factorial program in Java 8", TaskType.CODING); 18 | List tasks = Arrays.asList(t1, t2); 19 | 20 | Map taskIdToTaskMap = tasks.stream().collect(toMap(Task::getId, identity())); 21 | 22 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t1))); 23 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t2))); 24 | } 25 | ``` 26 | 27 | ## Usar una implementación diferente de Map 28 | 29 | La implementación por defecto usada por `Collectors.toMap()` es `HashMap`. Puedes especificar tu propia implementación de Map suministrando un proveedor. 30 | 31 | ```java 32 | @Test 33 | public void shouldCreateLinkedMapFromTaskList() throws Exception { 34 | Task t1 = new Task("Write blog on Java 8 Map improvements", TaskType.BLOGGING); 35 | Task t2 = new Task("Write factorial program in Java 8", TaskType.CODING); 36 | List tasks = Arrays.asList(t1, t2); 37 | 38 | Map taskIdToTaskMap = tasks.stream().collect(toMap(Task::getId, identity(), (k1, k2) -> k1, LinkedHashMap::new)); 39 | 40 | assertThat(taskIdToTaskMap, instanceOf(LinkedHashMap.class)); 41 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t1))); 42 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t2))); 43 | } 44 | ``` 45 | 46 | ## Manejando duplicados 47 | 48 | Una cosa que pasamos por alto en el último ejemplo es lo que pasaría si hubiese duplicados. Para controlar los duplicados tenemos el argumento extra. 49 | 50 | ```java 51 | @Test 52 | public void shouldHandleTaskListWithDuplicates() throws Exception { 53 | Task t1 = new Task("1", "Write blog on Java 8 Map improvements", TaskType.BLOGGING); 54 | Task t2 = new Task("1", "Write factorial program in Java 8", TaskType.CODING); 55 | List tasks = Arrays.asList(t1, t2); 56 | 57 | Map taskIdToTaskMap = tasks.stream().collect(toMap(Task::getId, identity())); 58 | 59 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t1))); 60 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t2))); 61 | } 62 | ``` 63 | 64 | El test fallará: 65 | 66 | ``` 67 | java.lang.IllegalStateException: Duplicate key Task{title='Write blog on Java 8 Map improvements', type=BLOGGING} 68 | ``` 69 | 70 | Puedes controlar el error especificando tu función de unión: 71 | 72 | ```java 73 | @Test 74 | public void shouldHandleTaskListWithDuplicates() throws Exception { 75 | Task t1 = new Task("1", "Write blog on Java 8 Map improvements", TaskType.BLOGGING); 76 | Task t2 = new Task("1", "Write factorial program in Java 8", TaskType.CODING); 77 | List tasks = Arrays.asList(t1, t2); 78 | Map taskIdToTaskMap = tasks.stream().collect(toMap(Task::getId, identity(), (k1, k2) -> k2)); 79 | assertThat(taskIdToTaskMap, hasEntry(notNullValue(), equalTo(t2))); 80 | } 81 | ``` 82 | 83 | ## Crear un Map a partir de tuplas 84 | 85 | ```java 86 | public static Map createMap(SimpleEntry... entries) { 87 | return Stream.of(entries).collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue)); 88 | } 89 | ``` 90 | 91 | [![Analytics](https://ga-beacon.appspot.com/UA-74043032-1/malobato/java8-the-missing-tutorial/06-map)](https://github.com/igrigorik/ga-beacon) 92 | -------------------------------------------------------------------------------- /es/07-building-functional-programs.md: -------------------------------------------------------------------------------- 1 | Construir Programas Funcionales 2 | -------- 3 | 4 | [![Analytics](https://ga-beacon.appspot.com/UA-74043032-1/malobato/java8-the-missing-tutorial/07-building-functional-programs)](https://github.com/igrigorik/ga-beacon) 5 | -------------------------------------------------------------------------------- /es/08-date-time-api.md: -------------------------------------------------------------------------------- 1 | API de Fecha y Hora 2 | ------- 3 | 4 | Hasta ahora en este [libro](https://github.com/malobato/java8-the-missing-tutorial) nos hemos centrado en la parte [funcional](02-lambdas.md) y en [aspectos](03-streams.md) de Java 8. Además hemos visto cómo diseñar mejores APIs usando [Optional](05-optionals.md) y [métodos por defecto y estáticos en interfaces](01-default-static-interface-methods.md). En este capítulo, aprenderemos sobre otro API nuevo que cambiará la forma en la que trabajamos con fechas: el API de Fecha y Hora. Casi todos los desarrolladores de Java estarán de acuerdo en que el soporte de fecha y hora anterior a Java 8 esta lejos de ser ideal y la mayoría de las veces tenemos que hacer uso de bibliotecas de terceros como [Joda-Time](http://www.joda.org/joda-time/) en nuestras aplicaciones. El nuevo API de fecha y hora esta muy influenciado por el API Joda-Time y si lo has usado te sentirás como en casa. 5 | 6 | ## ¿Qué tiene de malo el API de fecha anterior? 7 | 8 | Antes de aprender sobre el nuevo API de Fecha y Hora vamos a explicar porque no nos gusta el API de fecha anterior. Observa el código de abajo y trata de responder que imprimirá. 9 | 10 | ```java 11 | import java.util.Date; 12 | 13 | public class DateSucks { 14 | 15 | public static void main(String[] args) { 16 | Date date = new Date(12, 12, 12); 17 | System.out.println(date); 18 | } 19 | } 20 | ``` 21 | 22 | ¿Puedes responder que imprime? La mayoría de los programadores de Java pensarán que el programa escribe `0012-12-12` pero realmente escribe `Sun Jan 12 00:00:00 IST 1913`. Mi primera reacción cuando aprendí que el programa escribe esto fue... ¿qué demonios?. 23 | 24 | El código anterior tiene los siguientes problemas: 25 | 26 | 1. ¿Qué significa cada 12? Es el mes, el año y el día, el día, el mes y el año o cualquier otra combinación. 27 | 28 | 2. El índice del mes del API de fecha comienza en 0, por lo que diciembre es el mes 11. 29 | 30 | 3. El API de fecha es cíclico, p. ej. 12 será enero. 31 | 32 | 4. El año comienza en 1900 y dado que el mes también es cíclico el año se convierte en `1900 + 12 + 1 == 1913`. ¡Imagínate! 33 | 34 | 5. ¿Quién pidió la hora? Acabo de pedir la fecha y el programa también me imprime la hora. 35 | 36 | 6. ¿Por qué está la zona horaria? ¿Quién la pidió? La zona horaria es la zona horaria por defecto de la JVM, IST, Indian Standard Time en este ejemplo. 37 | 38 | > El API de fecha tiene cerca de 20 años y fue presentado con el JDK 1.0. Uno de los autores originales del API de fecha no es otro que el mismo James Gosling; el padre del lenguaje de programación Java. 39 | 40 | Existen muchos otros problemas de diseño del API de fecha como la mutabilidad, jerarquía de clases separadas para SQL, etc. En el JDK 1.1 se hizo un esfuerzo por proporcionar una mejor API con `Calendar` pero también estaba plagado de problemas similares de mutabilidad e índices comenzando por 0. 41 | 42 | ## El API de Fecha y Hora de Java 8 43 | 44 | El API de Fecha y Hora de Java 8 fue desarrollado como parte del JSR-310 y se encuentra en el paquete `java.time`. El API usa el principio de **diseño orientado a dominio** con clases de dominio como `LocalDate` o `LocalTime` que se usan para resolver problemas relacionados a sus dominios específicos de fecha y hora. Esto hace que el API sea claro y fácil de entender. El otro principio de diseño usado es el de **inmutabilidad**. Todas las clases del núcleo de `java.time` son inmutables por lo que evitan problemas de seguridad en hilos. 45 | 46 | ## Comenzando con el API de Fecha y Hora 47 | 48 | Las tres clases que más usarás del nuevo API son `LocalDate`, `LocalTime` y `LocalDateTime`. Su descripción es la que sugiere su nombre: 49 | 50 | * **LocalDate**: Representa una fecha sin hora ni zona horaria. 51 | 52 | * **LocalTime**: Representa una hora sin fecha ni zona horaria. 53 | 54 | * **LocalDateTime**: Es la combinación de las dos anteriores, la fecha y la hora sin la zona horaria. 55 | 56 | > Usaremos JUnit para mostrar los ejemplos. Primero escribiremos un caso de JUnit que explicará lo que tratamos de hacer y luego escribiremos el código para pasar el test. Los ejemplos se basarán en el gran presidente Indio -- [A.P.J Abdul Kalam](https://en.wikipedia.org/wiki/A._P._J._Abdul_Kalam). 57 | 58 | ### Kalam was born on 15th October 1931 59 | 60 | ```java 61 | import org.junit.Test; 62 | import java.time.LocalDate; 63 | import static org.hamcrest.CoreMatchers.equalTo; 64 | import static org.junit.Assert.assertThat; 65 | 66 | public class DateTimeExamplesTest { 67 | private AbdulKalam kalam = new AbdulKalam(); 68 | @Test 69 | public void kalamWasBornOn15October1931() throws Exception { 70 | LocalDate dateOfBirth = kalam.dateOfBirth(); 71 | assertThat(dateOfBirth.toString(), equalTo("1931-10-15")); 72 | } 73 | } 74 | ``` 75 | 76 | `LocalDate` has a static factory method `of()` that takes year, month, and date and gives you a `LocalDate`. To make this test pass, we will write `dateOfBirth` method in `AbdulKalam` class using `of()` method as shown below. 77 | 78 | ```java 79 | import java.time.LocalDate; 80 | import java.time.Month; 81 | 82 | public class AbdulKalam { 83 | public LocalDate dateOfBirth() { 84 | return LocalDate.of(1931, Month.OCTOBER, 15); 85 | } 86 | } 87 | ``` 88 | 89 | There is an overloaded `of()` method that takes month as integer instead of `Month` enum. I recommend using `Month` enum as it is more readable and clear. There are two other static factory methods to create `LocalDate` instances: `ofYearDay()` and `ofEpochDay()`. 90 | 91 | The `ofYearDay()` creates LocalDate instance from the year and day of year for example March 31st 2015 is the 90th day in 2015 so we can create LocalDate using `LocalDate.ofYearDay(2015, 90)`. 92 | 93 | ```java 94 | LocalDate january_21st = LocalDate.ofYearDay(2015, 21); 95 | System.out.println(january_21st); // 2015-01-21 96 | LocalDate march_31st = LocalDate.ofYearDay(2015, 90); 97 | System.out.println(march_31st); // 2015-03-31 98 | ``` 99 | 100 | The `ofEpochDay()` creates LocalDate instance using the epoch day count. The starting value of epoch is `1970-01-01`. So, `LocalDate.ofEpochDay(1)` will give `1970-01-02`. 101 | 102 | LocalDate instance provide many accessor methods to access different fields like year, month, dayOfWeek, etc. 103 | 104 | ```java 105 | @Test 106 | public void kalamWasBornOn15October1931() throws Exception { 107 | LocalDate dateOfBirth = kalam.dateOfBirth(); 108 | assertThat(dateOfBirth.getMonth(), is(equalTo(Month.OCTOBER))); 109 | assertThat(dateOfBirth.getYear(), is(equalTo(1931))); 110 | assertThat(dateOfBirth.getDayOfMonth(), is(equalTo(15))); 111 | assertThat(dateOfBirth.getDayOfYear(), is(equalTo(288))); 112 | } 113 | ``` 114 | 115 | You can create current date from the system clock using `now()` static factory method. 116 | 117 | ```java 118 | LocalDate.now() 119 | ``` 120 | 121 | ### Kalam was born at 01:15am 122 | 123 | ```java 124 | @Test 125 | public void kalamWasBornAt0115() throws Exception { 126 | LocalTime timeOfBirth = kalam.timeOfBirth(); 127 | assertThat(timeOfBirth.toString(), is(equalTo("01:15"))); 128 | } 129 | ``` 130 | 131 | `LocalTime` class is used to work with time. Just like `LocalDate`, it also provides static factory methods for creating its instances. We will use the `of()` static factory method giving it hour and minute and it will return LocalTime as shown below. 132 | 133 | ```java 134 | public LocalTime timeOfBirth() { 135 | return LocalTime.of(1, 15); 136 | } 137 | ``` 138 | 139 | There are other overloaded variants of `of()` method that can take second and nanosecond. 140 | 141 | > LocalTime is represented to nanosecond precision. 142 | 143 | You can print the current time of the system clock using `now()` method as shown below. 144 | 145 | ```java 146 | LocalTime.now() 147 | ``` 148 | 149 | You can also create instances of `LocalTime` from seconds of day or nanosecond of day using `ofSecondOfDay()` and `ofNanoOfDay()` static factory methods. 150 | 151 | Similar to `LocalDate`, `LocalTime` also provides accessor for its fields as shown below. 152 | 153 | ```java 154 | @Test 155 | public void kalamWasBornAt0115() throws Exception { 156 | LocalTime timeOfBirth = kalam.timeOfBirth(); 157 | assertThat(timeOfBirth.getHour(), is(equalTo(1))); 158 | assertThat(timeOfBirth.getMinute(), is(equalTo(15))); 159 | assertThat(timeOfBirth.getSecond(), is(equalTo(0))); 160 | } 161 | ``` 162 | 163 | ### Kalam was born on 15 October at 01:15 am 164 | 165 | When you want to represent both date and time together then you can use `LocalDateTime`. LocalDateTime also provides many static factory methods to create its instances. We can use `of()` factory method that takes a `LocalDate` and `LocalTime` and gives `LocalDateTime` instance as shown below. 166 | 167 | ```java 168 | public LocalDateTime dateOfBirthAndTime() { 169 | return LocalDateTime.of(dateOfBirth(), timeOfBirth()); 170 | } 171 | ``` 172 | 173 | There are many overloaded variants of `of` method which as arguments take year, month, day, hour, min, secondOfDay, nanosecondOfDay. 174 | 175 | ![LocalDateTime Of Methods](https://whyjava.files.wordpress.com/2015/10/localdatetime_of.png) 176 | 177 | To create current date and time using system clock you can use `now` factory method. 178 | 179 | ```java 180 | LocalDateTime.now() 181 | ``` 182 | 183 | ## Manipulating dates 184 | 185 | Now that we know how to create instances of `LocalDate`, `LocalTime`, and `LocalDateTime` let's learn how we can manipulate them. 186 | 187 | > LocalDate, LocalTime, and LocalDateTime are immutable so each time you perform a manipulation operation you get a new instance. 188 | 189 | ### Kalam 50th birthday was on Thursday 190 | 191 | ```java 192 | @Test 193 | public void kalam50thBirthDayWasOnThursday() throws Exception { 194 | DayOfWeek dayOfWeek = kalam.dayOfBirthAtAge(50); 195 | assertThat(dayOfWeek, is(equalTo(DayOfWeek.THURSDAY))); 196 | } 197 | ``` 198 | 199 | We can use `dateOfBirth` method that we wrote earlier with `plusYears()` on `LocalDate` instance to achieve this as shown below. 200 | 201 | ```java 202 | public DayOfWeek dayOfBirthAtAge(final int age) { 203 | return dateOfBirth().plusYears(age).getDayOfWeek(); 204 | } 205 | ``` 206 | 207 | There are similar `plus*()` variants for adding days, months, weeks to the value. 208 | 209 | Similar to `plus*()` methods there are `minus()` methods that allow you minus year, days, months from a `LocalDate` instance. 210 | 211 | ```java 212 | LocalDate today = LocalDate.now(); 213 | LocalDate yesterday = today.minusDays(1); 214 | ``` 215 | 216 | > Just like LocalDate, LocalTime and LocalDateTime also provide similar `plus*()` and `minus*()` methods. 217 | 218 | ### List all Kalam's birthdate DayOfWeek 219 | 220 | For this use-case, we will create an infinite stream of `LocalDate` starting from the Kalam's date of birth using the `Stream.iterate()` method. This method takes a starting value and a function that allows you to work on the initial seed value and return another value. We just incremented the year by 1 and return next year birthdate. Then we transformed `LocalDate` to `DayOfWeek` to get the desired output value. Finally, we limited our result set to the provided limit and collected Stream result into a List. 221 | 222 | ```java 223 | public List allBirthDateDayOfWeeks(int limit) { 224 | return Stream.iterate(dateOfBirth(), db -> db.plusYears(1)) 225 | .map(LocalDate::getDayOfWeek) 226 | .limit(limit) 227 | .collect(toList()); 228 | } 229 | ``` 230 | 231 | ## Duration and Period 232 | 233 | `Duration` and `Period` classes represents quantity or amount of time. 234 | 235 | **Duration** represents quantity or amount of time in seconds, nano-seconds, or days like 10 seconds. 236 | 237 | **Period** represents amount or quantity of time in years, months, and days. 238 | 239 | ### Calculate number of days kalam lived 240 | 241 | ```java 242 | @Test 243 | public void kalamLived30601Days() throws Exception { 244 | long daysLived = kalam.numberOfDaysLived(); 245 | assertThat(daysLived, is(equalTo(30601L))); 246 | } 247 | ``` 248 | 249 | To calculate the number of days kalam lived we can use `Duration` class. `Duration` has a factory method `between()` that takes two `LocalTime`, or `LocalDateTime` or `Instant` and gives a duration. The duration can then be converted to days, hours, seconds, etc. 250 | 251 | ```java 252 | public Duration kalamLifeDuration() { 253 | LocalDateTime deathDateAndTime = LocalDateTime.of(LocalDate.of(2015, Month.JULY, 27), LocalTime.of(19, 0)); 254 | return Duration.between(dateOfBirthAndTime(), deathDateAndTime); 255 | } 256 | 257 | public long numberOfDaysLived() { 258 | return kalamLifeDuration().toDays(); 259 | } 260 | ``` 261 | 262 | ### Kalam lived 83 years 9 months and 12 days 263 | 264 | ```java 265 | @Test 266 | public void kalamLifePeriod() throws Exception { 267 | Period kalamLifePeriod = kalam.kalamLifePeriod(); 268 | assertThat(kalamLifePeriod.getYears(), is(equalTo(83))); 269 | assertThat(kalamLifePeriod.getMonths(), is(equalTo(9))); 270 | assertThat(kalamLifePeriod.getDays(), is(equalTo(12))); 271 | } 272 | ``` 273 | 274 | We can use `Period` class to calculate number of years, months, and days kalam lived as shown below. Period's `between()` method works with `LocalDate` only. 275 | 276 | ```java 277 | public Period kalamLifePeriod() { 278 | LocalDate deathDate = LocalDate.of(2015, Month.JULY, 27); 279 | return Period.between(dateOfBirth(), deathDate); 280 | } 281 | ``` 282 | 283 | ## Printing and Parsing dates 284 | 285 | In our day-to-day applications a lot of times we have to parse a text format to a date or time or we have to print a date or time in a specific format. Printing and parsing are very common use cases when working with date or time. Java 8 provides a class `DateTimeFormatter` which is the main class for formatting and printing. All the classes and interfaces relevant to them resides inside the `java.time.format` package. 286 | 287 | ### Print Kalam birthdate in Indian date format 288 | 289 | In India, `dd-MM-YYYY` is the predominant date format that is used in all the government documents like passport application form. You can read more about Date and time notation in India on the [wikipedia](https://en.wikipedia.org/wiki/Date_and_time_notation_in_India). 290 | 291 | ```java 292 | @Test 293 | public void kalamDateOfBirthFormattedInIndianDateFormat() throws Exception { 294 | final String indianDateFormat = "dd-MM-YYYY"; 295 | String dateOfBirth = kalam.formatDateOfBirth(indianDateFormat); 296 | assertThat(dateOfBirth, is(equalTo("15-10-1931"))); 297 | } 298 | ``` 299 | 300 | The `formatDateofBirth()` method uses `DateTimeFormatter`'s `ofPattern()` method to create a new formatter using the specified pattern. All the main main date-time classes provide two methods: one for formatting, `format(DateTimeFormatter formatter)`, and one for parsing, `parse(CharSequence text, DateTimeFormatter formatter)`. 301 | 302 | ```java 303 | public String formatDateOfBirth(final String pattern) { 304 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); 305 | return dateOfBirth().format(formatter); 306 | } 307 | ``` 308 | 309 | For the common use cases, `DateTimeFormatter` class provides formatters as static constants. There are predefined constants for `BASIC_ISO_DATE` i.e **20111203** or `ISO_DATE` i.e. **2011-12-03**, etc that developers can easily use in their code. In the code shown below, you can see how to use these predefined formats. 310 | 311 | ```java 312 | @Test 313 | public void kalamDateOfBirthInDifferentDateFormats() throws Exception { 314 | LocalDate kalamDateOfBirth = LocalDate.of(1931, Month.OCTOBER, 15); 315 | assertThat(kalamDateOfBirth.format(DateTimeFormatter.BASIC_ISO_DATE), is(equalTo("19311015"))); 316 | assertThat(kalamDateOfBirth.format(DateTimeFormatter.ISO_LOCAL_DATE), is(equalTo("1931-10-15"))); 317 | assertThat(kalamDateOfBirth.format(DateTimeFormatter.ISO_ORDINAL_DATE), is(equalTo("1931-288"))); 318 | } 319 | ``` 320 | 321 | ### Parsing text to LocalDateTime 322 | 323 | Let's suppose we have to parse `15 Oct 1931 01:15 AM` to a LocalDateTime instance as shown in code below. 324 | 325 | ```java 326 | @Test 327 | public void shouldParseKalamDateOfBirthAndTimeToLocalDateTime() throws Exception { 328 | final String input = "15 Oct 1931 01:15 AM"; 329 | LocalDateTime dateOfBirthAndTime = kalam.parseDateOfBirthAndTime(input); 330 | assertThat(dateOfBirthAndTime.toString(), is(equalTo("1931-10-15T01:15"))); 331 | } 332 | ``` 333 | 334 | We will again use `DateTimeFormatter`'s `ofPattern()` method to create a new `DateTimeFormatter` and then use the `parse()` method of `LocalDateTime` to create a new instance of `LocalDateTime` as shown below. 335 | 336 | ```java 337 | public LocalDateTime parseDateOfBirthAndTime(String input) { 338 | return LocalDateTime.parse(input, DateTimeFormatter.ofPattern("dd MMM yyyy hh:mm a")); 339 | } 340 | ``` 341 | 342 | ## Advance date time manipulation with TemporalAdjusters 343 | 344 | In the **Manipulating dates** section, we learnt how we can use `plus*()` and `minus*()` methods to manipulate dates. Those methods are suitable for simple manipulation operations like adding or subtracting days, months, or years. Sometimes, we need to perform advance date time manipulation such as adjusting date to first day of next month or adjusting date to next working day or adjusting date to next public holiday then we can use `TemporalAdjusters` to meet our needs. Java 8 comes bundled with many predefined temporal adjusters for common scenarios. These temporal adjusters are available as static factory methods inside the `TemporalAdjusters` class. 345 | 346 | ```java 347 | LocalDate date = LocalDate.of(2015, Month.OCTOBER, 25); 348 | System.out.println(date);// This will print 2015-10-25 349 | 350 | LocalDate firstDayOfMonth = date.with(TemporalAdjusters.firstDayOfMonth()); 351 | System.out.println(firstDayOfMonth); // This will print 2015-10-01 352 | 353 | LocalDate firstDayOfNextMonth = date.with(TemporalAdjusters.firstDayOfNextMonth()); 354 | System.out.println(firstDayOfNextMonth);// This will print 2015-11-01 355 | 356 | LocalDate lastFridayOfMonth = date.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)); 357 | System.out.println(lastFridayOfMonth); // This will print 2015-10-30 358 | ``` 359 | * `firstDayOfMonth()` creates a new date set to first day of the current month. 360 | * `firstDayOfNextMonth()` creates a new date set to first day of next month. 361 | * `lastInMonth()` creates a new date in the same month with the last matching day-of-week. For example, last Friday in October. 362 | 363 | I have not covered all the temporal-adjusters please refer to the documentation for the same. 364 | ![TemporalAdjusters](https://whyjava.files.wordpress.com/2015/10/temporal-adjusters.png) 365 | 366 | ### Writing custom TemporalAdjuster 367 | 368 | You can write your own adjuster by implementing `TemporalAdjuster` functional interface. Let's suppose we have to write a TemporalAdjuster that adjusts today's date to next working date then we can use the `TemporalAdjusters` `ofDateAdjuster` method to adjust the current date to next working date as show below. 369 | 370 | ```java 371 | LocalDate today = LocalDate.now(); 372 | TemporalAdjuster nextWorkingDayAdjuster = TemporalAdjusters.ofDateAdjuster(localDate -> { 373 | DayOfWeek dayOfWeek = localDate.getDayOfWeek(); 374 | if (dayOfWeek == DayOfWeek.FRIDAY) { 375 | return localDate.plusDays(3); 376 | } else if (dayOfWeek == DayOfWeek.SATURDAY) { 377 | return localDate.plusDays(2); 378 | } 379 | return localDate.plusDays(1); 380 | }); 381 | System.out.println(today.with(nextWorkingDayAdjuster)); 382 | ``` 383 | 384 | [![Analytics](https://ga-beacon.appspot.com/UA-74043032-1/malobato/java8-the-missing-tutorial/08-date-time-api)](https://github.com/igrigorik/ga-beacon) 385 | -------------------------------------------------------------------------------- /es/09-completable-futures.md: -------------------------------------------------------------------------------- 1 | Completable Futures 2 | ------ 3 | 4 | 5 | 6 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/09-completable-futures)](https://github.com/igrigorik/ga-beacon) 7 | -------------------------------------------------------------------------------- /es/10-nashorn.md: -------------------------------------------------------------------------------- 1 | Nashorn: Run JavaScript on the JVM 2 | ----- 3 | 4 | ![Nashorn](https://upload.wikimedia.org/wikipedia/commons/7/7a/Dortmunder_Nashorn_-_Hell_wieherte_der_Hippogryph.jpg) 5 | 6 | Nashorn is a high-performance JavaScript runtime written in Java for the JVM. It allows developers to embed JavaScript code inside their Java applications and even use Java classes and methods from their JavaScript code. You can think it as an alternative to Google's V8 JavaScript engine. It is a successor to Rhino JavaScript runtime which came bundled with earlier JDK versions. Nashorn is written from scratch using new language features like JSR 292(Supporting Dynamically Typed Languages) and `invokedynamic`. 7 | 8 | From the Nashorn documentation: 9 | > Nashorn uses invokedynamic to implement all of its invocations. If an invocation has a Java object receiver, Nashorn attempts to bind the call to an appropriate Java method instead of a JavaScript function. Nashorn has full discretion about how it resolves methods. As an example, if it can't find a field in the receiver, it looks for an equivalent Java Bean method. The result is completely transparent for calls from JavaScript to Java. 10 | 11 | Currently, Nashorn supports [ECMAScript 5.1 specification](http://www.ecma-international.org/ecma-262/5.1/) and work is in progress to support [ECMAScript 6](http://www.ecma-international.org/ecma-262/6.0/) as well. Few ECMAScript 6 features like `let` and `const` are available in latest JDK 8 updates(40 and above) and we will cover them later in this chapter. 12 | 13 | In this chapter, we will cover the following: 14 | 15 | * Working with Nashorn command-line 16 | * Accessing Java classes and methods 17 | * Using external JavaScript libraries 18 | * Writing scripts 19 | * Using Nashorn from Java code 20 | * Using Java 8 features like Streams and Lambdas inside JavaScript code 21 | * Turning off Java language access 22 | 23 | 24 | ## Working with Nashorn command-line 25 | 26 | JDK 8 comes bundled with two command-line tools that can be used to work with Nashorn engine. These two command-line tools are `jrunscript` and `jjs`. `jjs` is recommended to be used when working with Nashorn so we will only discuss it. To use `jjs`, you have to add `jjs` to the path. On *nix machines, you can do that adding a symbolic link as shown below. 27 | 28 | ```bash 29 | $ cd /usr/bin 30 | $ ln -s $JAVA_HOME/bin/jjs jjs 31 | ``` 32 | Windows users can add `$JAVA_HOME/bin` to the path for easy access. 33 | 34 | Once you have set the symbolic link you can access `jjs` from your terminal. To check version of `jjs`, run the following command. 35 | 36 | ```bash 37 | $ jjs -v 38 | nashorn 1.8.0_60 39 | jjs> 40 | ``` 41 | 42 | It will render the version and then show `jjs>` prompt. You can view the full version of `jjs` by using `jjs -fv` command. 43 | 44 | To quit the `jjs` shell, you can use `Ctrl-C`. 45 | 46 | Once you are inside `jjs`, you can execute any JavaScript code as shown below. 47 | 48 | ```bash 49 | jjs> print("learning about Nashorn") 50 | learning about Nashorn 51 | ``` 52 | 53 | You can define functions as shown below. 54 | 55 | ``` 56 | jjs> function add(a,b) {return a + b;} 57 | ``` 58 | 59 | You can call the add function as shown below. 60 | 61 | ``` 62 | jjs> add(5,10) 63 | 15 64 | ``` 65 | 66 | ## Accessing Java classes and methods 67 | 68 | It is very easy to access Java classes from within Nashorn. Assuming you are inside the `jjs` shell, you can create an instance of HashMap as shown below. 69 | 70 | ```bash 71 | jjs> var HashMap = Java.type("java.util.HashMap") 72 | jjs> var userAndAge = new HashMap() 73 | jjs> userAndAge.put("shekhar",32) 74 | null 75 | jjs> userAndAge.put("rahul",33) 76 | null 77 | jjs> userAndAge.get("shekhar") 78 | 32 79 | ``` 80 | 81 | In the code shown above we have used `Java` global object to create HashMap object. `Java` global object has `type` method that takes a string with the fully qualified Java class name, and returns the corresponding `JavaClass` function object. 82 | 83 | ```bash 84 | jjs> HashMap 85 | [JavaClass java.util.HashMap] 86 | ``` 87 | 88 | The `var userAndAge = new HashMap()` is used to instantiate `java.util.HashMap` class using the `new` keyword. 89 | 90 | You can access values by either calling the `get` method or using the `[]` notation as shown below. 91 | 92 | ```bash 93 | jjs> userAndAge["shekhar"] 94 | 32 95 | ``` 96 | 97 | Similarly, you can work with other Java collections. To use an `ArrayList` you will write code as shown below. 98 | 99 | ```bash 100 | jjs> var List = Java.type("java.util.ArrayList") 101 | jjs> var names = new List() 102 | jjs> names.add("shekhar") 103 | true 104 | jjs> names.add("rahul") 105 | true 106 | jjs> names.add("sameer") 107 | true 108 | jjs> names.get(0) 109 | shekhar 110 | jjs> names[1] 111 | rahul 112 | ``` 113 | 114 | ### Accessing static methods 115 | 116 | To access static methods you have to first get the Java type using `Java.type` method and then calling method on `JavaClass` function object. 117 | 118 | ```bash 119 | jjs> var UUID = Java.type("java.util.UUID") 120 | jjs> 121 | jjs> UUID.randomUUID().toString() 122 | e4242b89-0e94-458e-b501-2fc4344d5498 123 | ``` 124 | 125 | You can sort list using `Collections.sort` method as shown below. 126 | 127 | ```bash 128 | jjs> var Collections = Java.type("java.util.Collections") 129 | jjs> 130 | jjs> Collections.sort(names) 131 | jjs> names 132 | [rahul, sameer, shekhar] 133 | jjs> 134 | ``` 135 | 136 | ## Using external JavaScript libraries 137 | 138 | Let's suppose we want to use an external JavaScript library in our JavaScript code. Nashorn comes up with a built-in function -- `load` that loads and evaluates a script from a path, URL, or script object. To use `lodash` library we can write code as shown below. 139 | 140 | ``` 141 | jjs> load("https://raw.github.com/lodash/lodash/3.10.1/lodash.js") 142 | 143 | jjs> _.map([1, 2, 3], function(n) { return n * 3; }); 144 | 3,6,9 145 | ``` 146 | 147 | ## Writing scripts 148 | 149 | You can use Nashorn extensions that enable users to write scripts that can use Unix shell scripting features. To enable shell scripting features, you have to start `jjs` with `-scripting` option as shown below. 150 | 151 | ```bash 152 | jjs -scripting 153 | jjs> 154 | ``` 155 | 156 | Now you have access to Nashorn shell scripting global objects. 157 | 158 | **$ARG:** This global object can be used to access the arguments passed to the script 159 | ``` 160 | $ jjs -scripting -- hello hey 161 | jjs> 162 | jjs> $ARG 163 | hello,hey 164 | ``` 165 | 166 | **$ENV:** A map containing all the current environment variables 167 | 168 | ```bash 169 | jjs> $ENV["HOME"] 170 | /Users/shekhargulati 171 | 172 | jjs> $ENV.JAVA_HOME 173 | /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home 174 | ``` 175 | 176 | **$EXEC:** launches processes to run commands 177 | 178 | ```bash 179 | jjs> $EXEC("pwd") 180 | /Users/shekhargulati/java8-the-missing-tutorial 181 | ``` 182 | 183 | ### Writing executable scripts 184 | 185 | You can use shebang(#!) at the beginning of the script to make a script file run as shell executable. Let's write a simple script that reads content of a file. We will use Java's `Files` and `Paths` API. 186 | 187 | ```javascript 188 | #!/usr/bin/jjs 189 | 190 | var Paths = Java.type("java.nio.file.Paths"); 191 | var Files = Java.type("java.nio.file.Files"); 192 | 193 | Files.lines(Paths.get($ARG[0])).forEach(function(line){print(line);}) 194 | ``` 195 | 196 | We will invoke it as 197 | 198 | ```bash 199 | $ jjs ch10/lines.js -- README.md 200 | ``` 201 | 202 | ## Using Nashorn from Java code 203 | 204 | To use Nashorn from inside Java code, you have to create an instance of ScriptEngine from `ScriptEngineManager` as shown below. Once you have `ScriptEngine` you can evaluate expressions. 205 | 206 | ```java 207 | import javax.script.ScriptEngine; 208 | import javax.script.ScriptEngineManager; 209 | import javax.script.ScriptException; 210 | 211 | public class NashornExample1 { 212 | 213 | public static void main(String[] args) throws ScriptException { 214 | ScriptEngineManager manager = new ScriptEngineManager(); 215 | ScriptEngine nashorn = manager.getEngineByName("nashorn"); 216 | Integer eval = (Integer) nashorn.eval("10 + 20"); 217 | System.out.println(eval); 218 | } 219 | } 220 | ``` 221 | 222 | 223 | Using bindings 224 | 225 | ```java 226 | import javax.script.*; 227 | import java.util.AbstractMap.SimpleEntry; 228 | import java.util.stream.Stream; 229 | 230 | import static java.util.stream.Collectors.toMap; 231 | 232 | public class NashornExample2 { 233 | 234 | public static void main(String[] args) throws ScriptException { 235 | ScriptEngineManager manager = new ScriptEngineManager(); 236 | ScriptEngine nashorn = manager.getEngineByName("nashorn"); 237 | 238 | Bindings bindings = new SimpleBindings(Stream.of( 239 | new SimpleEntry<>("a", 10), 240 | new SimpleEntry<>("b", 20)) 241 | .collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue))); 242 | Double eval = (Double) nashorn.eval("a + b", bindings); 243 | System.out.println(eval); 244 | } 245 | } 246 | ``` 247 | 248 | ## Using Java 8 features like Streams and Lambdas inside JavaScript code 249 | 250 | Java 8 supports lambdas and many API in JDK make use of them. Every collection in Java has `forEach` method that accepts a consumer. Consumer is an interface with one method. In Java, you can write following: 251 | 252 | ```java 253 | Arrays.asList("shekhar","rahul","sameer").forEach(name -> System.out.println(name)); 254 | 255 | // shekhar 256 | // rahul 257 | // sameer 258 | ``` 259 | 260 | In Nashorn, you can use them same API but you will pass JavaScript function instead as shown below. 261 | 262 | ```javascript 263 | jjs> var Arrays = Java.type("java.util.Arrays") 264 | jjs> Arrays.asList("shekhar","rahul","sameer") 265 | [shekhar, rahul, sameer] 266 | jjs> var names = Arrays.asList("shekhar","rahul","sameer") 267 | jjs> names.forEach(function(name){print(name);}) 268 | shekhar 269 | rahul 270 | sameer 271 | ``` 272 | 273 | You can also use Stream API with Nashorn as shown below. 274 | 275 | ``` 276 | jjs> names 277 | .stream().filter(function(name){return name.startsWith("s");}) 278 | .forEach(function(name){print(name);}) 279 | 280 | shekhar 281 | sameer 282 | ``` 283 | 284 | ## Turning off Java language access 285 | 286 | In case you need to disallow Java usage, you can very easily turn off by passing `--no-java` option to `jjs` as shown below. 287 | 288 | ``` 289 | → jjs --no-java 290 | jjs> 291 | jjs> var HashMap = Java.type("java.util.HashMap") 292 | :1 TypeError: null has no such function "type" 293 | ``` 294 | 295 | Now when you will try to use `java.util.HashMap` you will get `TypeError`. 296 | 297 | You can do the same with Java code as well. 298 | 299 | ```java 300 | 301 | import jdk.nashorn.api.scripting.ClassFilter; 302 | import jdk.nashorn.api.scripting.NashornScriptEngineFactory; 303 | 304 | import javax.script.ScriptEngine; 305 | import javax.script.ScriptException; 306 | 307 | public class NashornExample3 { 308 | 309 | public static void main(String[] args) throws ScriptException { 310 | NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); 311 | ScriptEngine nashorn = factory.getScriptEngine(new NoJavaFilter()); 312 | Integer eval = (Integer) nashorn.eval("var HashMap = Java.type('java.util.HashMap')"); 313 | System.out.println(eval); 314 | } 315 | 316 | private static class NoJavaFilter implements ClassFilter{ 317 | 318 | @Override 319 | public boolean exposeToScripts(String s) { 320 | return false; 321 | } 322 | } 323 | } 324 | ``` 325 | 326 | You will get following exception when you run this program. 327 | 328 | ``` 329 | Caused by: java.lang.ClassNotFoundException: java.util.HashMap 330 | at jdk.nashorn.internal.runtime.Context.findClass(Context.java:1032) 331 | at jdk.nashorn.internal.objects.NativeJava.simpleType(NativeJava.java:493) 332 | at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:322) 333 | at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:314) 334 | at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:310) 335 | at jdk.nashorn.internal.scripts.Script$\^eval\_.:program(:1) 336 | at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:640) 337 | at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:228) 338 | at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393) 339 | ``` 340 | 341 | 342 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/10-nashorn)](https://github.com/igrigorik/ga-beacon) 343 | -------------------------------------------------------------------------------- /es/11-tools.md: -------------------------------------------------------------------------------- 1 | Tools 2 | ----- 3 | 4 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/11-tools)](https://github.com/igrigorik/ga-beacon) 5 | -------------------------------------------------------------------------------- /es/12-annotations.md: -------------------------------------------------------------------------------- 1 | Annotation Improvements 2 | ------- 3 | 4 | There are couple of improvements made in the annotation mechanism in Java 8. These are: 5 | 6 | 1. Repeatable annotations 7 | 2. Type annotations 8 | 9 | > Code for this section is inside [ch12 package](https://github.com/shekhargulati/java8-the-missing-tutorial/tree/master/code/src/main/java/com/shekhargulati/java8_tutorial/ch12). 10 | 11 | ## Repeatable annotations 12 | 13 | Before Java 8, it was not possible to use same annotation twice at the same location i.e. you can't use annotation `@Foo` twice on a method. If you have used JPA then you would have use an annotation called `@JoinColumns` that allows wraps multiple `@JoinColumn` annotation as shown below. 14 | 15 | ```java 16 | @ManyToOne 17 | @JoinColumns({ 18 | @JoinColumn(name="ADDR_ID", referencedColumnName="ID"), 19 | @JoinColumn(name="ADDR_ZIP", referencedColumnName="ZIP") 20 | }) 21 | public Address getAddress() { return address; } 22 | ``` 23 | 24 | In Java 8, you can write the same as shown below because with Java 8 you can repeat an annotation multiple time at the same location. 25 | 26 | ```java 27 | @ManyToOne 28 | @JoinColumn(name="ADDR_ID", referencedColumnName="ID"), 29 | @JoinColumn(name="ADDR_ZIP", referencedColumnName="ZIP") 30 | public Address getAddress() { return address; } 31 | ``` 32 | 33 | 34 | ***With Java 8 you can use same annotation type multiple times possibly with different arguments at any location(class, method, field declaration) in your Java code.*** 35 | 36 | ### Writing your own repeatable Annotations 37 | 38 | To write your own repeatable annotation you have to do the following: 39 | 40 | **Step 1:** Create an annotation with `@Repeatable` annotation as shown below. `@Repeatable` annotation requires you to specify a mandatory value for the container type that will contain the annotation. We will create container annotation in step 2. 41 | 42 | ```java 43 | import java.lang.annotation.*; 44 | 45 | @Retention(RetentionPolicy.RUNTIME) 46 | @Target(ElementType.METHOD) 47 | @Repeatable(CreateVms.class) 48 | public @interface CreateVm { 49 | String name(); 50 | } 51 | ``` 52 | 53 | **Step 2:** Create a container annotation that holds the annotation. 54 | 55 | ```java 56 | @Retention(RetentionPolicy.RUNTIME) 57 | @Target(ElementType.METHOD) 58 | @interface CreateVms { 59 | CreateVm[] value(); 60 | } 61 | ``` 62 | 63 | Now you can use the annotation multiple times on any method as shown below. 64 | 65 | ```java 66 | @CreateVm(name = "vm1") 67 | @CreateVm(name = "vm2") 68 | public void manage() { 69 | System.out.println("Managing ...."); 70 | } 71 | ``` 72 | 73 | If you have to find all the repeatable annotations on a method then you can use `getAnnotationsByType()` method that is now available on `java.lang.Class` and `java.lang.reflect.Method`. To print all the vm names, you can write code as shown below. 74 | 75 | ```java 76 | CreateVm[] createVms = VmManager.class.getMethod("manage").getAnnotationsByType(CreateVm.class); 77 | Stream.of(createVms).map(CreateVm::name).forEach(System.out::println); 78 | ``` 79 | 80 | ## Type annotations 81 | 82 | You can now apply annotations at two more target locations -- TYPE_PARAMETER and TYPE_USE. 83 | 84 | ```java 85 | @Retention(RetentionPolicy.RUNTIME) 86 | @Target(value = {ElementType.TYPE_PARAMETER}) 87 | public @interface MyAnnotation { 88 | } 89 | ``` 90 | 91 | You can use it like 92 | 93 | ```java 94 | class MyAnnotationUsage<@MyAnnotation T> { 95 | } 96 | ``` 97 | 98 | You can also use annotations at type usage as shown below. 99 | 100 | ```java 101 | @Retention(RetentionPolicy.RUNTIME) 102 | @Target(value = {ElementType.TYPE_USE}) 103 | public @interface MyAnnotation { 104 | } 105 | ``` 106 | 107 | Then you can use them as shown below. 108 | 109 | ```java 110 | public static void doSth() { 111 | List<@MyAnnotation String> names = Arrays.asList("shekhar"); 112 | } 113 | ``` 114 | 115 | [![Analytics](https://ga-beacon.appspot.com/UA-59411913-3/shekhargulati/java8-the-missing-tutorial/12-annotations)](https://github.com/igrigorik/ga-beacon) 116 | -------------------------------------------------------------------------------- /es/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Shekhar Gulati 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /es/README.md: -------------------------------------------------------------------------------- 1 | Java 8: El Tutorial Perdido 2 | -------------- 3 | 4 | Java 8 ya no es un tema nuevo. Ya hay muchos buenos libros publicados sobre él. Todavía me encuentro con muchos desarrolladores de Java que no son conscientes del poder de Java 8. El objetivo de este micro libro es cubrir algunas de las características más importantes de Java 8 y como pueden ayudar al programador en su día a día. Esta basado en la serie de artículos del blog de Shekhar Gulati [7 days with Java 8](http://shekhargulati.com/7-days-with-java-8/). 5 | 6 | ## Contribuyendo a Java 8: El Tutorial Perdido 7 | 8 | ¡Por favor contribuye si detectas un error o algo que podría mejorarse! Lanza un [issue](https://github.com/shekhargulati/java8-the-missing-tutorial/issues) o envíale un pull request para mejorar. Serán bienvenidas contribuciones de todo tipo, incluyendo correcciones, añadidos, mejoras y traducciones. 9 | 10 | ## Tabla de Contenidos 11 | 12 | * [Métodos estáticos y por defecto en Interfaces](./01-default-static-interface-methods.md) 13 | * [Lambdas](./02-lambdas.md) 14 | * [Streams](./03-streams.md) 15 | * [Collectors](./04-collectors.md) 16 | * [Optionals](./05-optionals.md) 17 | * [Mejoras en Map](./06-map.md) 18 | * [Construyendo programas funcionales](./07-building-functional-programs.md): Aún sin escribir 19 | * [API de Fecha y Hora](./08-date-time-api.md) 20 | * [Completable Futures](./09-completable-futures.md): Aún sin escribir 21 | * [Nashorn](./10-nashorn.md) 22 | * [Herramientas](./11-tools.md): Aún sin escribir 23 | * [Mejoras en anotaciones](./12-annotations.md) 24 | 25 | ----------- 26 | Puedes seguir a Shekhar Gulati en twitter [https://twitter.com/shekhargulati](https://twitter.com/shekhargulati) o envíale un email a . También puedes leer su blog [http://shekhargulati.com/](http://shekhargulati.com/) 27 | 28 | [![Analytics](https://ga-beacon.appspot.com/UA-74043032-1/malobato/java8-the-missing-tutorial)](https://github.com/igrigorik/ga-beacon) 29 | -------------------------------------------------------------------------------- /java8-slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/java8-the-missing-tutorial/bb31c1d7d2bb60589f294c896c044b763c97a906/java8-slides.pdf --------------------------------------------------------------------------------