getAuthors() {
21 | return authors;
22 | }
23 |
24 | @Override
25 | public boolean equals(Object o) {
26 | if (this == o) return true;
27 | if (o == null || getClass() != o.getClass()) return false;
28 | Book book = (Book) o;
29 | return Objects.equals(getTitle(), book.getTitle()) && Objects.equals(getAuthors(), book.getAuthors());
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(getTitle(), getAuthors());
35 | }
36 |
37 | @Override
38 | public String toString() {
39 | return "Book{" +
40 | "title='" + title + '\'' +
41 | ", authors=" + authors +
42 | '}';
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/company/java11to16/java16/records/RecordsDemo.java:
--------------------------------------------------------------------------------
1 | package com.company.java11to16.java16.records;
2 |
3 | import static com.company.common.Utils.print;
4 |
5 | /**
6 | * Record are data-only immutable classes (thus have specific use cases)
7 | * They are a restricted (specialized) form of a class (like enums)
8 | * Not suitable for objects that are meant to change state, etc.
9 | *
10 | * Records are NOT:
11 | * - Boilerplate reduction mechanism
12 | *
13 | * Records generate constructors, getters, fields; equals, hashCode, toString
14 | *
15 | * Use cases:
16 | * - to model immutable data
17 | * - to hold read-only data in memory
18 | * - DTOs - Data Transfer Objects
19 | */
20 | public class RecordsDemo {
21 |
22 | public static void main(String[] args) {
23 | Product p1 = new Product("milk", 50);
24 | Product p2 = new Product("milk", 50);
25 |
26 | print(p1.price()); // without "get" prefix
27 | print(p1); // auto-generated toString() - Product[name=milk, price=50]
28 |
29 | print(p1 == p2); // false - different objects
30 | print(p1.equals(p2)); // true - values of fields (milk, 50) are compared by the auto-generated equals()/hashCode()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/company/java8to10/java9/CollectionFactoryMethods.java:
--------------------------------------------------------------------------------
1 | package com.company.java8to10.java9;
2 |
3 | import java.util.*;
4 |
5 | import static java.util.Map.entry;
6 |
7 | public class CollectionFactoryMethods {
8 |
9 | /**
10 | * New factory .of() methods
11 | */
12 | public static void main(String[] args) {
13 |
14 | // new factory methods - create unmodifiable (!) collections
15 | // i.e. set(), add() and other modifying methods throw UnsupportedOperationException
16 | List list = List.of(1, 2, 3);
17 | Set set = Set.of("a", "b");
18 | Map map = Map.of("key1", "val1", "key2", "val2");
19 |
20 |
21 | // Map.of() has 10 overloaded methods accepting up to 10 Key-Value pairs
22 | // To create a map with 11+ elements:
23 | Map test2 = Map.ofEntries(
24 | entry("key1", "val1"),
25 | entry("key2", "val2")
26 | // etc.
27 | );
28 |
29 |
30 | // older ways
31 | List listOld = Arrays.asList(1, 2, 3);
32 |
33 | Map mapOld = new HashMap<>();
34 | mapOld.put("key1", "val1");
35 | mapOld.put("key2", "val2");
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/company/java8to10/java9/process/ProcessId.java:
--------------------------------------------------------------------------------
1 | package com.company.java8to10.java9.process;
2 |
3 | import java.time.Instant;
4 | import java.util.Comparator;
5 |
6 | import static com.company.common.Utils.print;
7 |
8 | public class ProcessId {
9 |
10 | /**
11 | * The new Process API that allows you to manage and control operating system processes
12 | */
13 | public static void main(String... args) {
14 |
15 | long currentProcessId = ProcessHandle.current().pid();
16 | System.out.printf("pid: %s ", currentProcessId);
17 |
18 | print("================================");
19 |
20 | ProcessHandle.allProcesses()
21 | .map(ProcessHandle::info)
22 | .sorted(Comparator.comparing(info ->
23 | info.startInstant().orElse(Instant.MAX)))
24 | .limit(10)
25 | .forEach(ProcessId::printInfo);
26 |
27 | }
28 |
29 | private static void printInfo(ProcessHandle.Info info) {
30 | if (info.startInstant().isPresent() && info.command().isPresent() && info.user().isPresent()) {
31 | print("Started at: " + info.startInstant().get() +
32 | ", Command: " + info.command().get() +
33 | ", By user: " + info.user().get()
34 | );
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/src/main/java/com/company/java8to10/java9/OptionalApi.java:
--------------------------------------------------------------------------------
1 | package com.company.java8to10.java9;
2 |
3 | import com.company.common.Flight;
4 | import com.company.common.FlightSchedule;
5 |
6 | import java.util.Optional;
7 |
8 | import static com.company.common.Utils.print;
9 | import static java.util.Comparator.comparing;
10 |
11 | public class OptionalApi {
12 |
13 | /**
14 | * Two new methods on Optional
15 | * ifPresentOrElse(if present - do this, else do that)
16 | * or(substitute optional of the same type)
17 | */
18 | public static void main(String[] args) {
19 |
20 | // get me the earliest flight
21 | Optional earliestFlight =
22 | FlightSchedule.getFlights()
23 | .stream()
24 | .filter(f -> "Boston".equals(f.from()))
25 | .filter(f -> "San Francisco".equals(f.to()))
26 | .min(comparing(Flight::date));
27 |
28 |
29 | earliestFlight.ifPresentOrElse(x -> print("Flight found: " + earliestFlight),
30 | () -> print("Didn't find any flights for your search"));
31 |
32 |
33 | Optional alternative = earliestFlight.or(
34 | () -> Optional.of(new Flight("A", "B", "17-10-2023"))
35 | );
36 | print(alternative.get());
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/company/java17to20/java17/notes.txt:
--------------------------------------------------------------------------------
1 | https://openjdk.org/jeps/409
2 |
3 | A sealed class or interface can be extended or implemented only by those classes and interfaces permitted to do so.
4 |
5 | Benefits:
6 | 1) help enforce a well-defined and limited set of possible implementations - communicates INTENTION
7 | 2) Better security - help prevent unexpected or unauthorized subclassing and behavior from third-party code
8 |
9 | Rules:
10 | 1. A sealed class uses "permits" to allow other classes to subclass it.
11 | 2. A child class MUST either be final, sealed or non-sealed. (or code won't compile)
12 | 3. A permitted child class MUST extend the parent sealed class. Permitting without using the permit is now allowed.
13 | 4. The classes specified by permits must be located near the superclass:
14 | either in the same module (if the superclass is in a named module) (see Java 9 modularity)
15 | or in the same package (if the superclass is in the unnamed module).
16 |
17 | More on point 4:
18 | The motivation is that a sealed class and its (direct) subclasses are tightly coupled, since they must be compiled and maintained together.
19 | In a modular world, this means "same module"; in a non-modular world, the best approximation for this is "same package".
20 | If you use modules, you get some additional flexibility, because of the safety boundaries modules give you.
--------------------------------------------------------------------------------
/src/main/java/com/company/java8to10/java9/process/DestroyProcess.java:
--------------------------------------------------------------------------------
1 | package com.company.java8to10.java9.process;
2 |
3 | import static com.company.common.Utils.print;
4 |
5 | public class DestroyProcess {
6 |
7 | /**
8 | * On Windows: start the native Notepad app and run this program
9 | * For other Operating systems: open an equivalent app and change the .filter() line below
10 | */
11 | public static void main(String... args) {
12 |
13 | // 1) Find and print the process
14 | ProcessHandle notePadHandle =
15 | ProcessHandle.allProcesses()
16 | .filter(
17 | h -> h.info().command().map(s -> s.contains("Notepad.exe")).orElse(false)
18 | )
19 | .findFirst()
20 | .orElseThrow(() -> new RuntimeException("No matching handle found"));
21 |
22 | print(notePadHandle.info());
23 |
24 | // 2) Set up a listener in advance - if the process exits - inform us!
25 | notePadHandle.onExit() // Returns an async CompletableFuture (Promise)
26 | .thenAccept(h -> print("Notepad.exe was killed by Java!"));
27 |
28 | //3) End the process
29 | boolean shutdown = notePadHandle.destroy();
30 |
31 | // to avoid a race condition, make the current thread wait for the CompletableFuture before this program exits
32 | notePadHandle.onExit().join();
33 | print("Shutdown: " + shutdown);
34 | }
35 | }
--------------------------------------------------------------------------------
/src/main/java/com/company/java11to16/java11/httpclient/BasicLinkValidator.java:
--------------------------------------------------------------------------------
1 | package com.company.java11to16.java11.httpclient;
2 |
3 | import java.io.IOException;
4 | import java.net.URI;
5 | import java.net.http.HttpClient;
6 | import java.net.http.HttpRequest;
7 | import java.net.http.HttpResponse;
8 | import java.net.http.HttpResponse.BodyHandlers;
9 | import java.util.Optional;
10 |
11 | public class BasicLinkValidator {
12 |
13 | private static HttpClient client;
14 |
15 | public static void main(String[] args) throws IOException {
16 |
17 | client = HttpClient.newHttpClient();
18 |
19 | String links = """
20 | https://github.com/andrejs-ps
21 | https://httpstat.us/404
22 | https://httpstat.us/500
23 | """;
24 |
25 | var invalidResponses = links.lines()
26 | .map(BasicLinkValidator::getResponse)
27 | .filter(Optional::isPresent)
28 | .map(Optional::get)
29 | .filter(r -> r.statusCode() >= 400)
30 | .toList();
31 |
32 | invalidResponses.forEach(r ->
33 | System.out.printf("%s responded with %s\n", r.uri(), r.statusCode())
34 | );
35 | }
36 |
37 | private static Optional> getResponse(String link) {
38 | HttpRequest request = HttpRequest.newBuilder(URI.create(link)).build();
39 | try {
40 | var response = client.send(request, BodyHandlers.discarding());
41 | return Optional.of(response);
42 | } catch (IOException | InterruptedException e) {
43 | return Optional.empty();
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/com/company/java8to10/java9/StreamApi.java:
--------------------------------------------------------------------------------
1 | package com.company.java8to10.java9;
2 |
3 | import java.util.List;
4 | import java.util.function.Predicate;
5 |
6 | public class StreamApi {
7 |
8 | /**
9 | * New Stream API methods - takeWhile() and dropWhile()
10 | * They are like filter(), but with stop conditions
11 | */
12 | public static void main(String[] args) {
13 |
14 | // filter (from Java 8) - filter through the entire stream
15 | List list = List.of(1, 2, 3, 7, 9, 2, 6);
16 | Predicate p = i-> i < 5;
17 | list.stream()
18 | .filter(p)
19 | .forEach(System.out::print); // 1232
20 |
21 | System.out.println();
22 |
23 | // takeWhile - like filter(), but exit the stream as soon as the predicate does NOT match the 1st time
24 | list.stream()
25 | .takeWhile(p)
26 | .forEach(System.out::print); // 123
27 |
28 | System.out.println();
29 |
30 | // dropWhile - remove elements while the given predicate returns true
31 | // and stops removing (but keeps going) when the predicate returns false for the 1st time
32 | list.stream()
33 | .dropWhile(p)
34 | .forEach(System.out::print); // 7926
35 |
36 | System.out.println();
37 | // trick to extract a chunk of string
38 | String chunk = """
39 | Lorem ipsum
40 | ==========
41 | This is the text
42 | That we care about
43 | ==========
44 | Lorem ipsum
45 | """;
46 | // task: extract the text between "=========="
47 | String separator = "==========";
48 | chunk.lines()
49 | .dropWhile(l -> !l.contains(separator))
50 | .skip(1)
51 | .takeWhile(l -> !l.contains(separator))
52 | .forEach(System.out::println);
53 | }
54 |
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/company/java11to16/java15/TextBlocks.java:
--------------------------------------------------------------------------------
1 | package com.company.java11to16.java15;
2 |
3 | import static com.company.common.Utils.print;
4 |
5 | /**
6 | * Use cases for TextBlocks (What's New in Java 15 > Text Blocks in Practice)
7 | * - Blocks of text using markdown
8 | * - Testing, defining hard-coded JSON strings
9 | * - Simple templating
10 | */
11 | public class TextBlocks {
12 |
13 | public static void main(String[] args) {
14 | oldStyle();
15 | emptyBlock();
16 | jsonBlock();
17 | jsonMovedEndQuoteBlock();
18 | jsonMovedBracketsBlock();
19 | }
20 |
21 | private static void oldStyle() {
22 | print("******** Old style ********");
23 |
24 | String text = "{\n" +
25 | " \"name\": \"John Doe\",\n" +
26 | " \"age\": 45,\n" +
27 | " \"address\": \"Doe Street, 23, Java Town\"\n" +
28 | "}";
29 | print(text);
30 | }
31 |
32 | private static void emptyBlock() {
33 | print("******** Empty Block ********");
34 | String text = """
35 | """;
36 | print("|" + text + "|");
37 | }
38 |
39 | private static void jsonBlock() {
40 | print("******** JSON Block ********");
41 |
42 | String text = """
43 | {
44 | "name": "John Doe",
45 | "age": 45,
46 | "address": "Doe Street, 23, Java Town"
47 | }
48 | """; // <-- no indentation if char is aligned with first "
49 | print(text);
50 | }
51 |
52 | private static void jsonMovedEndQuoteBlock() {
53 | print("******** Json Moved End Quote Block ********");
54 |
55 | String text = """
56 | {
57 | "name": "John Doe",
58 | "age": 45,
59 | "address": "Doe Street, 23, Java Town"
60 | }
61 | """;
62 | print(text);
63 | }
64 |
65 | private static void jsonMovedBracketsBlock() {
66 | print("******** Json Moved Brackets Block ********");
67 |
68 | String text = """
69 | {
70 | "name": "John Doe",
71 | "age": 45,
72 | "address": "Doe Street, 23, Java Town"
73 | }
74 | """; // <-- indented by 2 spaces as it is aligned with third "
75 | print(text);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/com/company/java11to16/java14/SwitchExpressions.java:
--------------------------------------------------------------------------------
1 | package com.company.java11to16.java14;
2 |
3 | import static com.company.common.Utils.print;
4 |
5 | /**
6 | * "Switch Expressions" (SE) instead of "Switch Statements" (SS)
7 | * (Both can be used, but SE is better than SS)
8 | */
9 | public class SwitchExpressions {
10 |
11 | public static void main(String[] args) {
12 |
13 | oldStyleWithBreak(FruitType.APPLE);
14 |
15 | withSwitchExpression(FruitType.PEAR);
16 |
17 | switchExpressionWithReturn(FruitType.KIWI);
18 |
19 | switchWithYield(FruitType.PINEAPPLE);
20 | }
21 |
22 | // Old style is more verbose and error-prone (forgotten "break;" causes the switch to fall through)
23 | private static void oldStyleWithBreak(FruitType fruit) {
24 | print("==== Old style with break ====");
25 | switch (fruit) {
26 | case APPLE, PEAR:
27 | print("Common fruit");
28 | break;
29 | case PINEAPPLE, KIWI:
30 | print("Exotic fruit");
31 | break;
32 | default:
33 | print("Undefined fruit");
34 | }
35 | }
36 |
37 | private static void withSwitchExpression(FruitType fruit) {
38 | print("==== With switch expression ====");
39 | switch (fruit) {
40 | case APPLE, PEAR -> print("Common fruit");
41 | case PINEAPPLE -> print("Exotic fruit");
42 | default -> print("Undefined fruit");
43 | }
44 | }
45 |
46 | private static void switchExpressionWithReturn(FruitType fruit) {
47 | print("==== With return value ====");
48 |
49 | // or just "return switch" right away
50 | String text = switch (fruit) {
51 | case APPLE, PEAR -> "Common fruit";
52 | case PINEAPPLE -> "Exotic fruit";
53 | default -> "Undefined fruit";
54 | };
55 | print(text);
56 | }
57 |
58 | /**
59 | * "Yield" is like "return" but with an important difference:
60 | * "yield" returns a value and exits the switch statement. Execution stays within the enclosing method
61 | * "return" exits the switch and the enclosing method
62 | */
63 | // https://stackoverflow.com/questions/58049131/what-does-the-new-keyword-yield-mean-in-java-13
64 | private static void switchWithYield(FruitType fruit) {
65 | print("==== With yield ====");
66 | String text = switch (fruit) {
67 | case APPLE, PEAR -> {
68 | print("the given fruit was: " + fruit);
69 | yield "Common fruit";
70 | }
71 | case PINEAPPLE -> "Exotic fruit";
72 | default -> "Undefined fruit";
73 | };
74 | print(text);
75 | }
76 |
77 | public enum FruitType {APPLE, PEAR, PINEAPPLE, KIWI}
78 | }
--------------------------------------------------------------------------------