├── .classpath
├── .gitignore
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── Dockerfile
├── LICENSE
├── README.md
├── build.property
├── build
└── build.java
├── chapter00-genesis.jsh
├── chapter01-basic_types.jsh
├── chapter02-methods.jsh
├── chapter03-jshell_vs_java.jsh
├── chapter04-numbers.jsh
├── chapter05-control_flow.jsh
├── chapter07-interface.jsh
├── chapter08-lambda.jsh
├── chapter09-list_and_map.jsh
├── chapter10-string_formatting.jsh
├── chapter11-class_and_encapsulation.jsh
├── chapter12-equals_hashCode_toString.jsh
├── chapter13-contract.jsh
├── chapter13-modifiable_vs_mutable.jsh
├── chapter14-null_and_optional.jsh
├── chapter15-inheritance.jsh
├── chapter16-exception.jsh
├── chapter17-enum.jsh
├── chapter18-nested_classes.jsh
├── chapter20-generics.jsh
├── chapter21-wrapper.jsh
├── chapter22-variance.jsh
├── chapter23-limitation_of_generics.jsh
├── chapter25-stream.jsh
├── chapter26-collector.jsh
├── chapter30-data_structure.jsh
├── chapter31-sort.jsh
├── docker
├── Dockerfile
├── kernel.json
└── requirements.txt
├── guide
├── chapter00-genesis.md
├── chapter01-basic_types.md
├── chapter02-methods.md
├── chapter03-jshell_vs_java.md
├── chapter04-numbers.md
├── chapter05-control_flow.md
├── chapter07-interface.md
├── chapter08-lambda.md
├── chapter09-list_and_map.md
├── chapter10-string_formatting.md
├── chapter11-class_and_encapsulation.md
├── chapter12-equals_hashCode_toString.md
├── chapter13-contract.md
├── chapter13-modifiable_vs_mutable.md
├── chapter14-null_and_optional.md
├── chapter15-inheritance.md
├── chapter16-exception.md
├── chapter17-enum.md
├── chapter18-nested_classes.md
├── chapter20-generics.md
├── chapter21-wrapper.md
├── chapter22-variance.md
├── chapter23-limitation_of_generics.md
├── chapter25-stream.md
├── chapter26-collector.md
├── chapter30-data_structure.md
└── chapter31-sort.md
└── jupyter
├── .gitignore
├── chapter00-genesis.ipynb
├── chapter01-basic_types.ipynb
├── chapter02-methods.ipynb
├── chapter03-jshell_vs_java.ipynb
├── chapter04-numbers.ipynb
├── chapter05-control_flow.ipynb
├── chapter07-interface.ipynb
├── chapter08-lambda.ipynb
├── chapter09-list_and_map.ipynb
├── chapter10-string_formatting.ipynb
├── chapter11-class_and_encapsulation.ipynb
├── chapter12-equals_hashCode_toString.ipynb
├── chapter13-contract.ipynb
├── chapter13-modifiable_vs_mutable.ipynb
├── chapter14-null_and_optional.ipynb
├── chapter15-inheritance.ipynb
├── chapter16-exception.ipynb
├── chapter17-enum.ipynb
├── chapter18-nested_classes.ipynb
├── chapter20-generics.ipynb
├── chapter21-wrapper.ipynb
├── chapter22-variance.ipynb
├── chapter23-limitation_of_generics.ipynb
├── chapter25-stream.ipynb
├── chapter26-collector.ipynb
├── chapter30-data_structure.ipynb
└── chapter31-sort.ipynb
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | java-guide
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=14
5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
6 | org.eclipse.jdt.core.compiler.compliance=14
7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
11 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=enabled
12 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
13 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
14 | org.eclipse.jdt.core.compiler.release=disabled
15 | org.eclipse.jdt.core.compiler.source=14
16 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Dockerfile for java guide
2 | FROM forax/java-guide
3 |
4 | USER jovyan
5 |
6 | # Launch the notebook server
7 | ENV IJAVA_COMPILER_OPTS "--enable-preview --source=15"
8 | WORKDIR $HOME/jupyter
9 | CMD ["jupyter", "notebook", "--no-browser", "--ip", "0.0.0.0"]
10 |
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Rémi Forax
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # java-guide
2 | A guide to modern Java (Java 17)
3 |
4 | This guide is about learning Java from scratch if you know a bit of C or JavaScript.
5 |
6 | This is a Work in progress, it should be ready when the next LTS of Java (Java 17) will be released.
7 | All the codes run with Java 14 with the preview features enabled.
8 |
9 | > Note: if you are looking to only what's new in Java 14
10 | > [I have set of slides for that](https://github.com/forax/do-synthetic-methods-dream-of-electric-switch-expressions).
11 |
12 |
13 | ## Content
14 |
15 | 0. [genesis.md](guide/chapter00-genesis.md)
16 | 1. [basic_types.md](guide/chapter01-basic_types.md)
17 | 2. [methods.md](guide/chapter02-methods.md)
18 | 3. [jshell_vs_java.md](guide/chapter03-jshell_vs_java.md)
19 | 4. [numbers.md](guide/chapter04-numbers.md)
20 | 5. [control_flow.md](guide/chapter05-control_flow.md)
21 | 6. [interface.md](guide/chapter07-interface.md)
22 | 7. [lambda.md](guide/chapter08-lambda.md)
23 | 8. [list_and_map.md](guide/chapter09-list_and_map.md)
24 | 9. [string_formatting.md](guide/chapter10-string_formatting.md)
25 | 10. [class_and_encapsulation.md](guide/chapter11-class_and_encapsulation.md)
26 | 11. [equals_hashCode_toString.md](guide/chapter12-equals_hashCode_toString.md)
27 | 12. [contract.md](guide/chapter13-contract.md)
28 | 13. [modifiable_vs_mutable.md](guide/chapter13-modifiable_vs_mutable.md)
29 | 14. [null_and_optional.md](guide/chapter14-null_and_optional.md)
30 | 15. [inheritance.md](guide/chapter15-inheritance.md)
31 | 16. [exception.md](guide/chapter16-exception.md)
32 | 17. [enum.md](guide/chapter17-enum.md)
33 | 18. [nested_classes.md](guide/chapter18-nested_classes.md)
34 | 19. [array.md](guide/chapter19-array.md)
35 | 20. [implementing_interface.md](guide/chapter19-implementing_interface.md)
36 | 21. [generics.md](guide/chapter20-generics.md)
37 | 22. [wrapper.md](guide/chapter21-wrapper.md)
38 | 23. [variance.md](guide/chapter22-variance.md)
39 | 24. [limitation_of_generics.md](guide/chapter23-limitation_of_generics.md)
40 | 25. [stream.md](guide/chapter25-stream.md)
41 | 26. [collector.md](guide/chapter26-collector.md)
42 | 27. [data_structure.md](guide/chapter30-data_structure.md)
43 | 28. [sort.md](guide/chapter31-sort.md)
44 |
45 |
46 | ## Using Java Shell (jshell)
47 |
48 | Each chapter comes with executable examples that you can run using jshell.
49 |
50 | To get the examples, just clone this repository
51 | ```shell
52 | git clone http://github.com/forax/java-guide
53 | ```
54 |
55 | Then run jshell (at least Java 14 version)
56 | ```shell
57 | jshell --enable-preview
58 | ```
59 |
60 | Then you can copy paste the examples inside jshell and see by yourself.
61 |
62 | To quit use '/exit', to enable verbose error messages '/set feedback verbose', otherwise to get the help type '/help'
63 |
64 |
65 | ## Using Jupyter notebook
66 |
67 | ### on the cloud
68 | You can run it directly in your browser
69 | [](https://mybinder.org/v2/gh/forax/java-guide/master)
70 |
71 |
72 | ### or using docker
73 | You need to have docker already installed, then
74 |
75 | - get the docker image from dockerhub
76 | ```shell
77 | docker pull forax/java-guide
78 | ```
79 | - run the docker image in a container
80 | ```shell
81 | docker run -p 8888:8888 forax/java-guide
82 | ```
83 | - open your browser using the `tokenId` printed on the console
84 | ```shell
85 | firefox http://localhost:8888/?token=tokenId
86 | ```
87 |
88 |
89 | ### or install everything on your laptop
90 | You need to have python3 and Java 14 already installed, then
91 |
92 | - clone this repository
93 | ```shell
94 | git clone http://github.com/forax/java-guide
95 | cd java-guide
96 | ```
97 | - install [jupyter](https://jupyter.org/install)
98 | ```shell
99 | pip install notebook
100 | ```
101 | - install the [ijava 1.3.0](https://github.com/SpencerPark/IJava) kernel (from Spencer Park)
102 | ```shell
103 | wget https://github.com/SpencerPark/IJava/releases/download/v1.3.0/ijava-1.3.0.zip
104 | python3 install.py --sys-prefix
105 | ```
106 | - patch it with the repository file `kernel.json`
107 | list all kernels to see if the java kernel is installed
108 | ```shell
109 | jupyter kernelspec list
110 | ```
111 | then copy the file `kernel.json` from the folder `docker` to the java kernel directory
112 | ```shell
113 | cp docker/kernel.json /path/to/jupyter/kernels/java
114 | ```
115 | - set the env compiler option enabling the preview features
116 | ```shell
117 | export IJAVA_COMPILER_OPTS="--enable-preview -source 14"
118 | ```
119 | - run the notebook
120 | ```shell
121 | cd jupyter
122 | jupyter notebook
123 | ```
124 |
125 |
126 | ### Build markdown and jupyter files from jshell files
127 | The markdown files (.md) and the jupyter files (.ipynb) are derived/generated
128 | from the jshell files using a small Java script.
129 |
130 | Using java 14
131 | ```shell
132 | java --source 14 --enable-preview build/build.java
133 | ```
134 |
--------------------------------------------------------------------------------
/build.property:
--------------------------------------------------------------------------------
1 | # Configuation of build.java
2 | # 1. generate files
3 | # generate=x,y,z with x, y or z among markdown, notebook and slideshow
4 | # 2. choose to generate an index (optional)
5 | # index=x with x among markdown, notebook and slideshow
6 | # 3. customize folders
7 | # markdown.folder=f with f a folder
8 | # notebook.folder=f with f a folder
9 | # slideshow.folder=f with f a folder
10 |
11 | generate=markdown,notebook
12 | index=markdown
13 | markdown.folder=guide
14 | notebook.folder=jupyter
15 |
--------------------------------------------------------------------------------
/chapter00-genesis.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Genesis
6 | // In Java, there is strong division between primitive types like double that are written in lower case and
7 | // objects like String that have a name that starts with an uppercase letter.
8 |
9 | // ## Types
10 | // A primitive type is stored as value while an object is stored as
11 | // a reference (the address of the object in memory).
12 | // In Java, `var` creates a new variable
13 | var maxIntensity = 1.0; // it's a value
14 | var colorName = "black"; // it's a reference to String somewhere in memory
15 |
16 | // you can also indicate the type instead of `var`
17 | // if you are using var, you are asking the compiler to find the type for you.
18 | String colorName = "black";
19 |
20 | // ### System.out.println()
21 | // To print a value in Java we have a weird incantation `System.out.println()` that we will detail later.
22 | System.out.println(maxIntensity);
23 |
24 | // Primitive types and objects can be printed using the same incantation.
25 | System.out.println(colorName);
26 |
27 | // ### Concatenation with +
28 | // If we want to print a text followed by a value, we use the operator `+`.
29 | System.out.println("the value of colorName is " + colorName);
30 |
31 |
32 | // ## A record is a user defined type
33 | // here Light is defined as containing two components: a color (typed as a String) and
34 | // an intensity (typed as a 64-bit floating number double).
35 | record Light(String color, double intensity) {}
36 |
37 | // ### Object creation with `new`
38 | // To create an object in memory, we use the operator `new` followed by the value of each record component.
39 | // The following instruction creates a Light with "blue" as color and 1.0 as intensity.
40 | var blueLight = new Light("blue", 1.0);
41 | System.out.println(blueLight);
42 |
43 | // ### Record methods
44 | // To interact with an object in Java, we use methods, that are functions attached to an object.
45 | // To call a method, we use the operator `.` followed by the name of the method and its arguments.
46 | // A record automatically declares methods to access its components so Light declares two methods
47 | // color() and intensity().
48 |
49 | // By example to get the intensity of the object blueLight
50 | System.out.println(blueLight.intensity());
51 |
52 | // ### toString()
53 | // By default a record knows how to transform itself into a String
54 | // in Java, the method to transform an object to a String is named toString().
55 | // In fact, println() calls toString() if the argument is an object
56 | // so when using println(), calling explicitly toString() is not necessary.
57 | System.out.println(blueLight.toString());
58 | System.out.println(blueLight);
59 |
60 | // ### equals()
61 | // In Java, you can ask if two objects are equal, using the method equals(Object).
62 | // The return value is a boolean (a primitive type that is either true or false).
63 | var redLight = new Light("red", 0.5);
64 | var redLight2 = new Light("red", 0.5);
65 | System.out.println(blueLight.equals(redLight));
66 | System.out.println(redLight.equals(redLight2));
67 |
68 | // ### hashCode()
69 | // You can also ask to get an integer summary (a hash) of any objects.
70 | // This is used to speed up data structures (hash tables).
71 | // Two objects that are equals() must have the same hashCode().
72 | var greenLight = new Light("green", 0.2);
73 | var greenLight2 = new Light("green", 0.2);
74 | System.out.println(greenLight.hashCode());
75 | System.out.println(greenLight2.hashCode());
76 |
77 |
78 | // ## Summary
79 | // A `record` has components that are the parameters used to create an object
80 | // To create an object we use the operator `new` followed by the arguments of the
81 | // record components in the same order.
82 | // To interact with an object, we are using methods that are functions that you
83 | // call on an object using the operator `.`.
84 | // A Record defines methods to access the value of a component, and also
85 | // `toString()` to get the textual representation of an object and
86 | // `equals()` and `hashCode()` to test if two objects are equal.
87 |
--------------------------------------------------------------------------------
/chapter01-basic_types.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Basic Types
6 | // Java has two kinds of type,
7 | // - primitive types that are directly mapped to CPU basic types
8 | // - reference types that have the address of the object in memory
9 |
10 | // ## Primitive types
11 | // primitive types, written in lower case, have no method
12 |
13 | // ### boolean (true|false)
14 | var result = true;
15 | var anotherResult = false;
16 |
17 | // ### char (character)
18 | var firstLetter = 'j';
19 |
20 | // ### int (signed 32-bit integer)
21 | var numberOfLegs = 2;
22 |
23 | // ### double (64-bit floating point)
24 | var cost = 3.78;
25 |
26 | // ### long and float
27 | // some more exotic types that requires a suffix (`L` or `f`)
28 | // long (64-bit integers) and float (32-bit floating point numbers)
29 | var longValue = 123L;
30 | var floatValue = 123.5f;
31 |
32 | // ### byte and short
33 | // you also have byte (a signed 8-bit integer) and short (a signed 16-bit short integer)
34 | // that are only useful to use less memory when defining an object
35 | record CompactHeader(byte tag, short version) {}
36 |
37 | // when used in variables, they are promoted to a 32-bit integer.
38 | // In the following code, `result` is a 32-bit integer (so an int)
39 | short value = 12;
40 | var result = value + value;
41 |
42 |
43 | // ### primitive conversions
44 | // You have automatic conversions if there is no loose of precision
45 | // and converting to double or float is always allowed
46 | int intValue = 13;
47 | long longValue = intValue;
48 |
49 | // you can force conversion in the opposite direction using a cast
50 | // supplementary bits will be shaved (use with reluctance)
51 | long longValue = 1_000_000_000_000L;
52 | int intValue = (int) longValue;
53 | System.out.println(intValue);
54 |
55 |
56 | // ## Objects
57 | // All other types are objects, there are two special types, String and arrays
58 | // that are object but considered as built-in by the compiler
59 |
60 | // ### String
61 | // A String that stores a text (a sequence of characters) is delimited
62 | // by two doublequotes
63 | var text = "hello";
64 | System.out.println(text);
65 |
66 | // a String can also span several lines, it's called a __text block__
67 | // and starts and ends with 3 double quotes
68 | var multilineText = """
69 | This is
70 | a multilines string
71 | """;
72 | System.out.println(multilineText);
73 |
74 | // The indentation is determined by the alignment compared to position of the last """
75 | // By example, to have an indentation of two spaces
76 | var multilineText = """
77 | This is
78 | a multilines string
79 | indented by two spaces
80 | """;
81 | System.out.println(multilineText);
82 |
83 | // Strings have a lot of methods, here is some of them
84 | // length of a String
85 | System.out.println("hello".length());
86 |
87 | // to upper/lower case
88 | // Locale.ROOT here ask for a result independent of the OS language
89 | System.out.println("hello".toUpperCase(Locale.ROOT));
90 | System.out.println("hello".toLowerCase(Locale.ROOT));
91 |
92 | // repeat the same pattern
93 | System.out.println("|*|".repeat(3));
94 |
95 | // char at an index (starting with index 0)
96 | System.out.println("hello".charAt(0));
97 |
98 | // index of a character
99 | System.out.println("hello".indexOf('l'));
100 | System.out.println("hello".indexOf('o'));
101 |
102 | // primitive to String
103 | // The fastest and easy way to convert a primitive value to a String is
104 | // to use the string concatenation (see chapter 'string formatting' for more)
105 | System.out.println("" + 3);
106 | System.out.println("" + 7.6);
107 |
108 | // String to primitive
109 | // There are a bunch of static methods in Boolean, Integer or Double
110 | // (see chapter 'wrapper' for more info)
111 | System.out.println(Integer.parseInt("3"));
112 | System.out.println(Double.parseDouble("7.6"));
113 |
114 |
115 | // ### Array
116 | // an array initialized with zeros (false, 0, 0.0, etc)
117 | var intArray = new int[2];
118 |
119 | // An array initialized with some default values
120 | // Because a value like `2` or `3` can be an numeric type
121 | // (an `int`, a `long`, a `short`, etc)
122 | // you have to specify the type of the array when you create it
123 | var intArray = new int[] {2, 3 };
124 | var longArray = new long[] { 2, 3 };
125 |
126 | // you can use the operator [] to access or change the value
127 | // of an array at a specific index
128 | System.out.println(intArray[0]);
129 | intArray[0] = 42;
130 | System.out.println(intArray[0]);
131 |
132 | // trying to access an array out of its bound raised an exception
133 | intArray[-1] = 42; // throws IndexOutOfBoundsException
134 |
135 | // and a special syntax to get the length of an array
136 | // Notice that there is no parenthesis when calling length,
137 | // we will see later why.
138 | var arrayLength = intArray.length;
139 | System.out.println(arrayLength);
140 |
141 | // arrays have methods like \toString()` or `equals()` but
142 | // they are not implemented correctly, we will see later why
143 | System.out.println(intArray);
144 | System.out.println(new int[] {42}.equals(new int[] {42}));
145 |
146 |
147 | // ### On arrays
148 | // We don't use array much in Java, we have more
149 | // powerful object like List, that we will see later
150 | var intList = List.of(2, 3);
151 |
152 |
153 | // ## Static methods
154 | // Because primitive types and arrays have (almost) no method,
155 | // if you want to play with them you have to use static methods.
156 | // A static method is a function that is declared on a type somewhere
157 | // that you can call using the syntax `SomeWhere.methodName(arg0, arg1, arg2)`
158 |
159 | // by example to transform a String to an int, we call the method
160 | // parseInt stored in the type `java.lang.Integer`
161 | var resultAsInt = java.lang.Integer.parseInt("42");
162 | System.out.println(resultAsInt);
163 |
164 | // To transform an array to a text, there is the static method toString
165 | // on the type `java.util.Arrays`
166 | var text = java.util.Arrays.toString(intArray);
167 | System.out.println(text);
168 |
--------------------------------------------------------------------------------
/chapter02-methods.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Methods
6 | // Methods are action to execute on an object
7 |
8 | // The method syntax starts with the return type, the name of the method,
9 | // then its parameters
10 | // The first parameter named `this` is a special keyword indicating the value
11 | // before the `.`
12 | // The keyword `return` indicates the return value of the method.
13 | record Rectangle(int width, int height) {
14 | boolean hasTheSameHeight(Rectangle this, Rectangle rectangle) {
15 | return this.height == rectangle.height;
16 | }
17 | }
18 |
19 | // A method is called on an instance (an object) of a record
20 | // here rectangle1 and rectangle2 are instances of Rectangle
21 | var rectangle1 = new Rectangle(2, 3);
22 | var rectangle2 = new Rectangle(4, 3);
23 | System.out.println(rectangle1.hasTheSameHeight(rectangle2));
24 |
25 | // The value before the `.`, here rectangle1, is called the receiver,
26 | // and is stored as the parameter `this` for the method hasTheSameHeight().
27 |
28 | // If you don't declare `this` as first parameter, it is declared implicitly
29 | // so the record Rectangle below is equivalent to the record Rectangle above
30 | record Rectangle(int width, int height) {
31 | boolean hasTheSameHeight(Rectangle rectangle) { // implicit this
32 | return this.height == rectangle.height;
33 | }
34 | }
35 |
36 | // In Java, it's very unusual to declare `this` explicitly.
37 | // Moreover, if inside a method you access to a variable which is not a parameter
38 | // it will be automatically prefixed by 'this.', so the code can be simplified to
39 | record Rectangle(int width, int height) {
40 | boolean hasTheSameHeight(Rectangle rectangle) {
41 | return height == rectangle.height; // no this.height needed !
42 | }
43 | }
44 | var rectangle1 = new Rectangle(2, 3);
45 | var rectangle2 = new Rectangle(4, 3);
46 | System.out.println(rectangle1.hasTheSameHeight(rectangle2));
47 |
48 |
49 | // ### void
50 | // There is also a special type named 'void' if the method return no value
51 | record Rectangle(int width, int height) {
52 | void hello() {
53 | System.out.println("hello i'm " + width + " x " + height);
54 | }
55 | }
56 | new Rectangle(4, 5).hello();
57 |
58 |
59 | // ## instance methods vs static methods
60 | // In Java, there are two kinds of methods, the one attached to an instance (the 'this')
61 | // that are called 'instance' methods and the one that are independent of an instance
62 | // that are called 'static' method. A static method is prefixed by the keyword static.
63 |
64 | // Unlike an instance method, a static method has no 'this'
65 | // by example, we can create a static method createSquare() that create a Rectangle
66 | // with the same width and the same height
67 |
68 | record Rectangle(int width, int height) {
69 | int area() {
70 | return width * height;
71 | }
72 | static Rectangle createSquare(int side) {
73 | return new Rectangle(side, side);
74 | }
75 | }
76 |
77 | // because a static method is independent of an instance, you can not access to the
78 | // record component inside a static method because the value of the component depends
79 | // on the instance (rectangle1 and rectangle2 has not the same width or height).
80 |
81 | // To call a static method, you call it on the record name
82 | var square1 = Rectangle.createSquare(3);
83 | System.out.println(square1.area());
84 |
85 |
86 | // ### static methods are useful to share code
87 | // by example, to calculate the length of the diagonal of a Rectangle, one can write
88 | record Rectangle(int width, int height) {
89 | double diagonal() {
90 | return Math.sqrt(width * width + height * height);
91 | }
92 | }
93 | var rectangle2 = new Rectangle(4, 3);
94 | System.out.println(rectangle2.diagonal());
95 |
96 | // it can also be written using a static method `pow2()` to share some code
97 | record Rectangle(int width, int height) {
98 | double diagonal() {
99 | return Math.sqrt(pow2(width) + pow2(height));
100 | }
101 | static int pow2(int value) {
102 | return value * value;
103 | }
104 | }
105 | var rectangle2 = new Rectangle(4, 3);
106 | System.out.println(rectangle2.diagonal());
107 |
108 |
109 | // In fact, there is already a static method named hypot in java.lang.Math
110 | // that computes the hypotenuse, so Rectangle can be written like this
111 | record Rectangle(int width, int height) {
112 | double diagonal() {
113 | return Math.hypot(width, height);
114 | }
115 | }
116 | var rectangle2 = new Rectangle(4, 3);
117 | System.out.println(rectangle2.diagonal());
118 |
119 |
120 |
--------------------------------------------------------------------------------
/chapter03-jshell_vs_java.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Jshell vs Java
6 | // We are using jshell, jshell has mostly the same behavior as Java
7 | // but because it's interactive it can do more
8 |
9 |
10 | // ## Re-defining records or variables
11 | // you can define a record or a variable several times, jshell will use the most recent one
12 | record Point(int x, int y) { }
13 | var p = new Point(2, 3);
14 |
15 | record Point(int x, int y) {
16 | int distanceInX(int anotherX) {
17 | return Math.abs(x - anotherX);
18 | }
19 | }
20 | var p = new Point(2, 3);
21 | System.out.println(p.distanceInX(0));
22 |
23 | // and you can also directly write a method outside a record,
24 | // it will act as a static method
25 | void hello() {
26 | System.out.println("hello !");
27 | }
28 | hello();
29 |
30 |
31 | // ## Special commands
32 | // There are a bunch of special commands that starts with '/'
33 | // you can use /help if you want to know more
34 |
35 | // By example, you have also a list of the packages automatically imported
36 | /import
37 |
38 | // and you can import new package dynamically
39 | import java.util.zip.*
40 |
41 | // note that unlike import in Python or #include in C, Java import doesn't load any code,
42 | // it says that is you search a name of a type, here are the packages to search.
43 | // If a type appear in more than one package, an error will be reported and you will
44 | // have to import the type explicitly (without any *)
45 | // by example, to import a List of the package java.util
46 | import java.util.List;
47 |
48 |
49 | // # Entry Point
50 | // With jshell, the code is executed from top to bottom
51 | // In Java, when executing `java Hello`, the launcher looks for a static method
52 | // named `main` that
53 | // - must be visible from the outside (`public`)
54 | // - takes an array of String (`String[]`) and
55 | // - returns no value (`void`)
56 | record Hello() {
57 | public static void main(String[] args) {
58 | System.out.println("Hello !");
59 | }
60 | }
61 |
62 | // unlike in C, the first argument is `args[0]`.
63 |
--------------------------------------------------------------------------------
/chapter04-numbers.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Numbers
6 | // This chapter in not specific to Java but more on how integers and floating point numbers
7 | // work on CPUs like Intel 64-bit or ARM 64-bit.
8 |
9 |
10 | // ## Integers
11 |
12 | // ### Dividing by zero
13 | // If you try to divide by 0, you get an exception
14 | System.out.println(1 / 0);
15 | System.out.println(1 % 0); // remainder of the division
16 |
17 | // ### Overflow
18 | // Given that integers are represented using a fixed number of bits,
19 | // there is a minimum/maximum number that can be represented
20 | System.out.println("max " + Integer.MAX_VALUE);
21 | System.out.println("min " + Integer.MIN_VALUE);
22 |
23 | // integers can overflow, if a positive integers is too big, it becomes negative
24 | System.out.println(Integer.MAX_VALUE + 1);
25 |
26 | // and vice versa
27 | System.out.println(Integer.MIN_VALUE - 1);
28 |
29 | // In Java, you have safe alternatives that are slower but throw an exception
30 | // if the computation overflow
31 | Math.addExact(Integer.MAX_VALUE, 1);
32 | Math.subtractExact(Integer.MIN_VALUE, 1);
33 |
34 |
35 | // You can notice that the minimum value is one less than the maximum value
36 | // in absolute value so `Math.abs()` has an overflow issue
37 | // because -Integer.MIN_VALUE is not Integer.MAX_VALUE
38 | System.out.println(Math.abs(Integer.MIN_VALUE));
39 |
40 | // When trying to find the middle between two values, the calculation may also overflow
41 | // so the result becomes nagative
42 | int middle(int value1, int value2) {
43 | return (value1 + value2) / 2;
44 | }
45 | System.out.println(middle(Integer.MAX_VALUE, 1));
46 |
47 | // In this specific case, you can recover from the overflow by using
48 | // the triple shift operator `>>>` that doesn't consider the sign bit as a sign bit
49 | // but as a bit which is part of the value
50 | int middle(int value1, int value2) {
51 | return (value1 + value2) >>> 1;
52 | }
53 | System.out.println(middle(Integer.MAX_VALUE, 1));
54 |
55 |
56 | // ## Double precision floating point numbers
57 | // The computation using floating point in not precise because not all values are
58 | // directly representable so the CPU will use the closest value.
59 | // It's like using 3.14 when you ask for π
60 |
61 | // So when you do a computation, the error propagates and becomes visible
62 | System.out.println(0.1 + 0.2);
63 |
64 | // When you print a double, there is a trick, only some decimals will be printed
65 | // so you may think the value is fully represented that but that's just an illusion
66 | System.out.println(1.0 / 3.0);
67 |
68 | // On way to see the trick is to ask a float (32-bit), to be printed as a double (64-bit).
69 | System.out.println(1.0f / 3.0f);
70 | System.out.println(Float.toString(1.0f / 3.0f));
71 | System.out.println(Double.toString(1.0f / 3.0f)); // damn i'm unmasked
72 |
73 |
74 | // ### No exception
75 | // The computation is said `secured` so instead of having an exception thrown
76 | // when you divide by 0, you have three special values of double to represent the result
77 | // of the computation
78 |
79 | // +Infinity
80 | System.out.println(1.0 / 0.0);
81 | System.out.println(Double.POSITIVE_INFINITY);
82 |
83 | // -Infinity
84 | System.out.println(-1.0 / 0.0);
85 | System.out.println(Double.NEGATIVE_INFINITY);
86 |
87 | // Not A Number
88 | System.out.println(0.0 / 0.0);
89 | System.out.println(Double.NaN);
90 |
91 | // ### NaN
92 | // Not a Number is very weird, because by definition, it's the number which is not equal to itself
93 |
94 | // Don't use == to test NaN, it will not work
95 | System.out.println(Double.NaN == Double.NaN);
96 |
97 | // The only way to test is NaN is NaN is to test if it is equals to itself (by definition)
98 | boolean isNotANumber(double x) {
99 | return x != x;
100 | }
101 | System.out.println(isNotANumber(Double.NaN));
102 |
103 | // An equivalent static method to isNotANumber already exist in Double, `Double.isNaN()`
104 | System.out.println(Double.isNaN(Double.NaN));
105 |
106 |
107 | // ### Record and NaN
108 | // To avoid the issue of a record r not equals to itself because it has a component
109 | // that contains NaN the implementation of `equals()` for a record checks
110 | // the raw bytes of the double after all NaN (yes internally there are several possible
111 | // representation of NaN) are collapsed into one so testing if two records are equals works as expected !
112 | record MagicBeer(double content) { }
113 | var beer1 = new MagicBeer(Double.NaN);
114 | var beer2 = new MagicBeer(Double.NaN);
115 | System.out.println(beer1.equals(beer2));
116 |
--------------------------------------------------------------------------------
/chapter05-control_flow.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Expression and Control Flow
6 | // Most of the control flow syntax of Java comes from C with some enhancements
7 |
8 | // ## Variables
9 | // if you explicitly type a variable you can declare a variable without initializing it
10 | int x;
11 |
12 | // but a variable can be used only after being initialized
13 | x = 3;
14 | System.out.println(x);
15 |
16 | // block of code
17 | // a variable declared in a block of code, can not be used outside that block
18 | {
19 | var value = 42;
20 | }
21 | // value can not be used here !
22 |
23 |
24 | // ## If
25 |
26 | // ### Test with `if`
27 | // the construct `if` execute the block of code that follow if the condition in between
28 | // the parenthesis is true
29 | void oldEnough(int age) {
30 | if (age >= 21) {
31 | System.out.println("you are old enough to drink a beer");
32 | }
33 | }
34 | oldEnough(22);
35 |
36 | // ### Test with `if ... else`
37 | // you can append the construct `else` after an `if` to execute a block of code
38 | // if the condition is not true
39 | void oldEnough(int age) {
40 | if (age >= 21) {
41 | System.out.println("you are old enough to drink a beer");
42 | } else {
43 | System.out.println("too bad for you !");
44 | }
45 | }
46 | oldEnough(17);
47 |
48 |
49 | // ## Switch
50 |
51 | // ### Test with a `switch` statement
52 | // There are two forms of switch, a switch statement is a switch that doesn't
53 | // produce a value. For those, the `default` case is not mandatory
54 | void vehicle(int wheels) {
55 | switch(wheels) {
56 | case 1 -> System.out.println("monocycle !");
57 | case 2 -> System.out.println("bicycle !");
58 | case 3, 4 -> System.out.println("car !");
59 | default -> {
60 | // if there are several lines
61 | System.out.println("whaat !");
62 | }
63 | }
64 | }
65 | vehicle(3);
66 |
67 | // ### Test with a `switch` expression
68 | // A switch that produces a valeur is a switch expresssion. Given that a
69 | // value need to be produced, a `default` case is mandatory
70 | String vehicle(int wheels) {
71 | return switch(wheels) {
72 | case 1 -> "monocycle !";
73 | case 2 -> "bicycle !";
74 | case 3, 4 -> "car !";
75 | default -> "whaat !";
76 | };
77 | }
78 | System.out.println(vehicle(3));
79 |
80 | // you can switch on integers, strings and enums
81 | int doors(String kind) {
82 | return switch(kind) {
83 | case "smart" -> 3;
84 | case "sedan", "hatchback" -> 5;
85 | default -> { throw new IllegalArgumentException(kind); }
86 | };
87 | }
88 | System.out.println(doors("sedan"));
89 |
90 | // ### Test with a `switch` compatible with C
91 | // You can use the C compatible switch too, using `:` instead of `->`
92 | // (you can not mix them) but in that case don't forget to ends
93 | // each case with a `break`.
94 | void vehicle(int wheels) {
95 | switch(wheels) {
96 | case 1:
97 | System.out.println("monocycle !");
98 | break;
99 | case 2:
100 | System.out.println("bicycle !");
101 | break;
102 | case 3:
103 | case 4:
104 | System.out.println("car !");
105 | break;
106 | default:
107 | System.out.println("whaat !");
108 | }
109 | }
110 | vehicle(3);
111 |
112 |
113 | // ## Instanceof
114 |
115 | // ### `instanceof`
116 | // instanceof test the class of a value at runtime, if instanceof succeeds,
117 | // the value is stored in the variable declared as last argument
118 | record Car(int seats) {}
119 | record Bus(int capacity) {}
120 | int maxPersons(Object value) {
121 | if (value instanceof Car car) {
122 | return car.seats();
123 | }
124 | if (value instanceof Bus bus) {
125 | return bus.capacity();
126 | }
127 | return 0;
128 | }
129 | System.out.println(maxPersons(new Car(4)));
130 | System.out.println(maxPersons(new Bus(32)));
131 |
132 | // ### `instanceof` with no variable declaration
133 | // if you don't need the variable declaration, you can omit it
134 | void printKind(Object value) {
135 | if (value instanceof Car) {
136 | System.out.println("it a car");
137 | }
138 | if (value instanceof Bus bus) {
139 | System.out.println("it a bus");
140 | }
141 | }
142 | printKind(new Car(4));
143 |
144 |
145 | // ## Loops
146 |
147 | // ### `while` loop
148 | // a `while` loop execute the block of code while the condition in between parenthesis is true
149 | void printFirstIntegers(int n) {
150 | var i = 0;
151 | while(i < n) {
152 | System.out.println(i);
153 | i++;
154 | }
155 | }
156 | printFirstIntegers(5);
157 |
158 | // ### `for` loop
159 | // a for loop is a convenient way to write a `while` loop in case you do a while on a variable,
160 | // so instead of using the `while` loop above, one can write this for loop
161 | void printFirstIntegers(int n) {
162 | for(var i = 0; i < n; i++) {
163 | System.out.println(i);
164 | }
165 | }
166 | printFirstIntegers(5);
167 |
168 |
169 |
170 | // ### `for` loop on array or list
171 | // Java as a special loop for iterating over the content of an array or a list,
172 | // it using the keyword `for`, but the declared variable contains each element one by one
173 | var list = List.of("iron man", "captain america", "black panther");
174 | for(var value: list) {
175 | System.out.println(value);
176 | }
177 |
178 |
179 | // ### On loops
180 | // Most of the loops can also be abstracted using higher order constructs
181 | // if you don't understand that code now, don't panic, we will come back
182 | // to that later
183 |
184 | // using `IntStream.range()`
185 | IntStream.range(0, 5).forEach(System.out::println);
186 |
187 | // using `List.forEach()`
188 | list.forEach(System.out::println);
189 |
--------------------------------------------------------------------------------
/chapter07-interface.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Interface
6 | // Java is a typed language, even if you don't explicitly write a type
7 | // the compiler you compute the type of every variables
8 | // Once you start to want to mix several records, you may need to declare
9 | // common type between records, such type are known as interface
10 |
11 | // ## The problem
12 | // let say we have a Square and Rectangle, and both have a method `area()`
13 | record Square(int side) {
14 | public double area() {
15 | return side * side;
16 | }
17 | }
18 | record Rectangle(int width, int height) {
19 | public double area() {
20 | return width * height;
21 | }
22 | }
23 |
24 | // let create a list of a square and a rectangle
25 | var figures = List.of(new Square(2), new Rectangle(3, 4));
26 |
27 | // try to loop over the elements of the figures to print the area doesn't compile
28 | for(var figure: figures) {
29 | System.out.println(figure.area());
30 | }
31 |
32 | // The problem is that compiler try to find the type of the element of the list
33 | // and find that they are java.lang.Object, and Object has no method area()
34 | // so the code does not compile
35 |
36 |
37 | // ### Interface and abstract method
38 | // The idea is to introduce a type Figure has a common type for Square and Rectangle.
39 | // In Java, we use the keyword `interface` for that.
40 |
41 | // The method `area()` in Figure is not a classical method with some code because
42 | // the code is defined in Square and Rectangle. It's an `abstract` method.
43 | // The definition of the method is present but the code has to be implemented by the
44 | // records that implement the interface
45 | interface Figure {
46 | public abstract double area();
47 | }
48 |
49 | // and declare that a Square and a Rectangle are a kind of Figure
50 | // using the keyword `implements`
51 | record Square(int side) implements Figure {
52 | public double area() {
53 | return side * side;
54 | }
55 | }
56 | record Rectangle(int width, int height) implements Figure {
57 | public double area() {
58 | return width * height;
59 | }
60 | }
61 |
62 | // Now, the list is correctly typed as a list of figure (`List`)
63 | // so looping over the figures to call `area()` works
64 | List figures = List.of(new Square(2), new Rectangle(3, 4));
65 | for(var figure: figures) {
66 | System.out.println(figure.area());
67 | }
68 |
69 | // An interface is a common type that you need to declare when you want to
70 | // call the same method on different records
71 | // At runtime, when you call a method of the interface, the virtual machine calls
72 | // the correct implementation (this is called polymorphism)
73 |
74 |
75 | // ## Static method
76 | // Like a record, an interface can have `static` methods
77 | interface Figure {
78 | public abstract double area();
79 | public static Figure createASquare(int side) {
80 | return new Square(side);
81 | }
82 | }
83 | var aSquare = Figure.createASquare(3);
84 | System.out.println(aSquare);
85 |
86 |
87 | // ## Default method
88 | // Inside an interface, the instance methods are implicitly abstract,
89 | // if we want to declare a method with some code in it, we have to use
90 | // the keyword `default`.
91 | // By example, we can write a method `isBig` that is true if the area is big enough.
92 | interface Figure {
93 | public abstract double area();
94 | public default boolean isBig() {
95 | return area() >= 10;
96 | }
97 | }
98 | System.out.println(new Square(2).isBig());
99 | System.out.println(new Rectangle(3, 4).isBig());
100 |
101 | // Because a default method is declared on the interface, all records that
102 | // implement that interface will have that method. Default methods are named like this
103 | // because if a record that implements the interface doesn't define the method,
104 | // the method will be provided by default.
105 |
106 |
107 | // ## Functional interface
108 | // An interface with only one abstract method is equivalent to a function type.
109 | // We name this kind of interfaces, _functional_ interfaces.
110 | // They can be implemented by two special constructs.
111 |
112 | // ### Lambda
113 | // The parameter are declared in between the parenthesis and the body of the method
114 | // is declared after the arrow (like the expression switch).
115 | interface Figure {
116 | public abstract double area();
117 | }
118 | Figure anotherFigure = () -> 4;
119 | System.out.println(anotherFigure.area());
120 |
121 | // and rewrite the method rectangularTriangle()
122 | // You can notice that a lambda can access to the parameter `width` and `height`
123 | Figure rectangularTriangle(int width, int height) {
124 | return () -> width * height / 2.0;
125 | }
126 | var triangle = rectangularTriangle(3, 4);
127 | System.out.println(triangle.area());
128 |
129 |
130 | // ### Method Reference
131 | // In case of the method already exists instead of
132 | // calling it inside a lambda, we can make a reference on it using the operator ::
133 | // (notice that EquilaterlaTriangle doesn't implement Figure)
134 | record EquilateralTriangle(int side) {
135 | double area() {
136 | return Math.sqrt(3) * side * side / 4.0;
137 | }
138 | }
139 | var equilateral = new EquilateralTriangle(2);
140 |
141 | // so instead of
142 | var figures = List.of(new Square(2), () -> equilateral.area());
143 | for(var figure: figures) {
144 | System.out.println(figure.area());
145 | }
146 |
147 | // you can use a method reference
148 | var figures = List.of(new Square(2), equilateral::area);
149 | for(var figure: figures) {
150 | System.out.println(figure.area());
151 | }
152 |
153 |
154 | // More about lambdas and method references in the following chapter.
155 |
--------------------------------------------------------------------------------
/chapter10-string_formatting.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # String formatting
6 | // There are several ways to concatenate/format objects to strings in Java,
7 | // mostly depending if there are a fixed number of values or
8 | // if the values are in a list or any other data structures.
9 |
10 | // Let say we have some friends
11 | record Friend(String name) {}
12 | var bob = new Friend("bob");
13 | var ana = new Friend("ana");
14 | var jul = new Friend("jul");
15 |
16 |
17 | // ## With a fixed number of values
18 | // If there is a fixed number of value, the concatenation using '+' is the
19 | // most readable (ok, when your are used to) and the fastest
20 |
21 |
22 | // ### Concatenation with +
23 | // Just do a '+' between the different values,
24 | // this code is heavily optimized and will allocate only one String
25 | System.out.println(bob.name() + ", " + ana.name() + ", " + jul.name());
26 |
27 |
28 | // ### Concatenation with String.format()
29 | // If you want more control on the formatting, you can use `String.format`
30 | // that reuse the C formatting style
31 | // But the method `format()` is quite slow.
32 | System.out.println(String.format("%s, %s, %s", bob, ana, jul));
33 | System.out.printf("%s, %s, %s\n", bob, ana, jul);
34 |
35 |
36 | // ## with a variable number of values
37 | // If there is a variable numbers of values, you have two cases,
38 | // depending if it's a collection of String or not
39 |
40 | var strings = List.of("bob", "ana", "jul");
41 | var friends = List.of(bob, ana, jul);
42 |
43 | // ### Concatenation with a +
44 | // Never use '+' in this case, because the compiler is not smart enough
45 | // to reuse the same buffer of characters for the whole loop, so it will
46 | // create a new String for each loop trip.
47 | String concatenate(List> list) {
48 | var string = "";
49 | var separator = "";
50 | for(var item: list) {
51 | string = string + separator + item; // creates two many strings, ahhhh
52 | separator = ", ";
53 | }
54 | return string;
55 | }
56 | System.out.println(concatenate(strings));
57 | System.out.println(concatenate(friends));
58 |
59 | // ### Concatenation with a StringBuilder
60 | // A StringBuilder is a modifiable version of String with an expandable buffer
61 | // of characters. There is no notion of separators
62 | String concatenate(List> list) {
63 | var builder = new StringBuilder();
64 | var separator = "";
65 | for(var item: list) {
66 | builder.append(separator).append(item);
67 | separator = ", ";
68 | }
69 | return builder.toString();
70 | }
71 | System.out.println(concatenate(strings));
72 | System.out.println(concatenate(friends));
73 |
74 | // > Don't use '+' inside a call to `append()`, you already have a StringBuilder,
75 | // so use append() instead
76 |
77 |
78 | // ### Concatenation with String.join()
79 | // If you have an array of strings or a collection of strings, `String.join`
80 | // is the simplest way to concatenate the items with a separator
81 |
82 | System.out.println(String.join(", ", strings));
83 |
84 |
85 | // ### Concatenation with a StringJoiner
86 | // If you don't have a list of strings by a list of objects, you can use the
87 | // `StringJoiner` which let you specify a separator and is implemented
88 | // using expandable buffer of strings (`StringJoiner.add` only accepts strings).
89 |
90 | String concatenate(List> list) {
91 | var joiner = new StringJoiner(", ");
92 | list.forEach(item -> joiner.add(item.toString()));
93 | return joiner.toString();
94 | }
95 | System.out.println(concatenate(strings));
96 | System.out.println(concatenate(friends));
97 |
98 |
99 | // ### Concatenation with a Stream
100 | // If you use a `Stream` and the collector `joining`, it will use a `StringJoiner` internally.
101 |
102 | import java.util.stream.Collectors;
103 | System.out.println(strings.stream().collect(Collectors.joining(", ")));
104 | System.out.println(friends.stream().map(Friend::toString).collect(Collectors.joining(", ")));
105 |
--------------------------------------------------------------------------------
/chapter11-class_and_encapsulation.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Class and encapsulation
6 |
7 | // Let's say i want to create a library of books,
8 | // so we need a record Book and a record Library that stores the books has a list
9 | record Book(String title, String author) { }
10 | record Library(List books) { }
11 |
12 | // and use it that way
13 | var book = new Book("DaVinci Code", "Dan Brown");
14 | var books = new ArrayList();
15 | books.add(book);
16 | var library = new Library(books);
17 | System.out.println(library);
18 |
19 | // The problem with a Library declared like this in that the library is not really
20 | // in control of the books inside itself, one can write
21 | books.add(new Book("Effective Java", "Joshua Bloch"));
22 | System.out.println(library);
23 |
24 | // The result is surprising, you can add books in the library without calling
25 | // a method of the library which make the code hard to debug because changing
26 | // an object has an effect to another object.
27 |
28 |
29 | // ## Encapsulation principle
30 | // In a pure functional language, the language doesn't allow you to
31 | // do side effect. In an OO language, if you want to survive, the idea is
32 | // to limit the functions that can do side effects to the instance methods.
33 |
34 | // This idea is named the encapsulation principle and is sum up by this sentence
35 | // > The only way to change the value of an object is to use one of the methods of this object.
36 |
37 | // In Java, the way to ensure the encapsulation principle is to do information hiding,
38 | // i.e. to separate the __public__ API part (what the user code can use) from the __private__
39 | // implementation part (how the class is implemented).
40 |
41 | // This separation is done by using a special syntax named __class__ that allows
42 | // to precisely control of the visibility of its members.
43 |
44 | // ## Class
45 | // A class defines
46 | // - private fields that is like a record component but not visible by the user code
47 | // - a public constructor (Library), that guarantee that any objects will be correctly initialized
48 | // - public and private instance and static methods
49 |
50 | // ### Unmodifiable class
51 | class Library {
52 | private final List books;
53 | public Library(List books) {
54 | this.books = List.copyOf(books);
55 | }
56 | public String toString() {
57 | return "Library " + books.toString();
58 | }
59 | }
60 | var library = new Library(books);
61 | System.out.println(library);
62 |
63 | // Now changing the list of books has no effect on the library
64 | // because the field `books` and the argument of the constructor `books` are different references
65 | books.remove(new Book("DaVinci Code", "Dan Brown"));
66 | System.out.println(library);
67 |
68 | // You can notice that the constructor has no return type, it's because it's always void.
69 |
70 | // The field 'books' is declared final which means must be initialized
71 | // in the constructor (and not changed afterward) so we are sure that in toString(),
72 | // the field 'books' has been initialized.
73 |
74 | // Unlike a record, the method equals()/hashCode() and toString() are not provided and has
75 | // to be hand written. We will see how to implement them later.
76 |
77 |
78 | // ### Modifiable class
79 | // The code above is an unmodifiable implementation of Library.
80 | // We can also write a mutable version with the caveat that using it
81 | // as element of a list or a map is not recommended.
82 | class ModifiableLibrary {
83 | private final ArrayList books;
84 | public ModifiableLibrary() {
85 | books = new ArrayList<>();
86 | }
87 | public void add(Book book) {
88 | Objects.requireNonNull(book);
89 | books.add(book);
90 | }
91 | public String toString() {
92 | return "ModifiableLibrary " + books.toString();
93 | }
94 | }
95 | var library = new ModifiableLibrary();
96 | library.add(new Book("DaVinci Code", "Dan Brown"));
97 | System.out.println(library);
98 | library.add(new Book("Effective Java", "Joshua Bloch"));
99 | System.out.println(library);
100 |
101 |
102 | // ### Modifiable class and accessors
103 | // An error sometime seen is to add a method to get the content of the library
104 | // and forget that it may expose the private list of books
105 | class ModifiableLibrary {
106 | private final ArrayList books;
107 | public ModifiableLibrary() {
108 | books = new ArrayList<>();
109 | }
110 | public void add(Book book) {
111 | Objects.requireNonNull(book);
112 | books.add(book);
113 | }
114 | public List getBooks() {
115 | return books;
116 | }
117 | public String toString() {
118 | return "ModifiableLibrary " + books.toString();
119 | }
120 | }
121 |
122 | // The following code breaks the encapsulation because you can
123 | // modify the library without calling a method of the Library
124 | // (`add()` is called on the List not on the Library)
125 | var library = new ModifiableLibrary();
126 | var books = library.getBooks();
127 | books.add(new Book("DaVinci Code", "Dan Brown"));
128 |
129 | // One solution is to return a copy, or better a non modifiable view
130 | // of the internal list of books
131 | class ModifiableLibrary {
132 | private final ArrayList books;
133 | public ModifiableLibrary() {
134 | books = new ArrayList<>();
135 | }
136 | public void add(Book book) {
137 | books.add(book);
138 | }
139 | public List getBooks() {
140 | return Collections.unmodifiableList(books);
141 | }
142 | public String toString() {
143 | return "ModifiableLibrary " + books.toString();
144 | }
145 | }
146 | var library = new ModifiableLibrary();
147 | var books = library.getBooks();
148 | books.add(new Book("DaVinci Code", "Dan Brown"));
149 |
150 | // The best solution being to not have a method `getBook()` at all,
151 | // the less code you write the less bug you have.
152 | // So please don't write getters and setters unless you really need them.
153 |
154 |
155 | // ## Record constructor
156 | // Records also provides ways to customize the code to respect the
157 | // encapsulation principle
158 | // Here, we only need to change the canonical constructor
159 | record Library(List books) {
160 | public Library(List books) {
161 | this.books = List.copyOf(books);
162 | }
163 | }
164 |
165 |
166 | // To summarize, a class is a general mechanism to describe how things
167 | // are implemented and make a separation between what is publicly visible
168 | // and what is privately implemented to make the code working.
169 | // A record is a special case when there is no separation, everything is public.
170 |
--------------------------------------------------------------------------------
/chapter12-equals_hashCode_toString.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Implementing equals()/hashCode() and toString()
6 |
7 | // ## Why equals()/hashCode() and toString() are important
8 | // Those methods are used by the data structures (list, map) to implement operations
9 | // like add(), contains(), etc. So most of the data structure doesn't work
10 | // correctly if `equals()`/`hashCode()` and `toString()` are not correctly written
11 | // on the element.
12 |
13 | // By example, `ArraysList.contains(value)` uses `value.equals()`,
14 | // `HashMap.get(key)` uses both `key.hashCode()` and `key.equals()`.
15 |
16 |
17 | // ## Default implementations from java.lang.Object
18 | // Object defines several methods and provide a default implementation for them
19 | // - `boolean equals(Object)`
20 | // test if two objects are equals (same type and same values), but
21 | // the default implementation do an ==, so only check if the two objects are at
22 | // the same address in memory
23 | // - `int hashCode()`
24 | // return a summary of the content of the object as an int
25 | // the default implementation choose a random number when the object is created
26 | // - `String toString()`
27 | // return a textual representation of the object
28 | // the default implementation return a concatenation of the
29 |
30 | // So the default implementations only ensure that an object is equals to itself.
31 |
32 |
33 | // ## Writing your own equals()/hashCode()
34 | // - `equals()` must be valid for any object and returns false if it's not the right type
35 | // so it starts with an `instanceof` and calls `equals()` if the field value
36 | // is a reference.
37 | // - `hashCode()` delegates to the hashCode of the field value
38 | class User {
39 | private final String name;
40 | public User(String name) {
41 | this.name = Objects.requireNonNull(name);
42 | }
43 | public boolean equals(Object o) {
44 | return o instanceof User user &&
45 | name.equals(user.name);
46 | }
47 | public int hashCode() {
48 | return name.hashCode();
49 | }
50 | public String toString() {
51 | return "User " + name;
52 | }
53 | }
54 | var user1 = new User("Bob");
55 | var user2 = new User("Bob");
56 | System.out.println(user1.equals(user2));
57 | System.out.println(user1.hashCode() == user2.hashCode());
58 | System.out.println(user1);
59 |
60 |
61 | // ### With two fields
62 | // - `equals()`, it's better to first check the primitive fields because a primitive check
63 | // is usually faster than a call to `equals()`.
64 | // - `hashCode()` can use the exclusive or `^` to mix the hash code.
65 | class User {
66 | private final String name;
67 | private final int age;
68 | public User(String name, int age) {
69 | this.name = Objects.requireNonNull(name);
70 | this.age = age;
71 | }
72 | public boolean equals(Object o) {
73 | return o instanceof User user &&
74 | age == user.age && name.equals(user.name);
75 | }
76 | public int hashCode() {
77 | return name.hashCode() ^ age;
78 | }
79 | public String toString() {
80 | return "User " + name + " " + age;
81 | }
82 | }
83 | var user1 = new User("Bob", 31);
84 | var user2 = new User("Bob", 31);
85 | System.out.println(user1.equals(user2));
86 | System.out.println(user1.hashCode() == user2.hashCode());
87 | System.out.println(user1);
88 |
89 |
90 | // ### With several fields
91 | // - equals(), as said in chapter 'basic_types', array.equals() doesn't work,
92 | // Arrays.equals() should be used instead
93 | // - hashCode(), `Object.hash` compute the hash of several values separated by commas.
94 | class User {
95 | private final String name;
96 | private final int age;
97 | private final String login;
98 | private final char[] password;
99 | public User(String name, int age, String login, char[] password) {
100 | this.name = Objects.requireNonNull(name);
101 | this.age = age;
102 | this.login = Objects.requireNonNull(login);
103 | this.password = password.clone();
104 | }
105 | public boolean equals(Object o) {
106 | return o instanceof User user &&
107 | age == user.age && name.equals(user.name) &&
108 | login.equals(user.login) && Arrays.equals(password, user.password);
109 | }
110 | public int hashCode() {
111 | return Objects.hash(name, age, login, Arrays.hashCode(password));
112 | }
113 | public String toString() {
114 | return "User " + name + " " + age + " " + login + " " + "*".repeat(password.length);
115 | }
116 | }
117 | var user1 = new User("Bob", 31, "bob", "df15cb4e019ec2eac654fb2e486c56df285c8c7b".toCharArray());
118 | var user2 = new User("Bob", 31, "bob", "df15cb4e019ec2eac654fb2e486c56df285c8c7b".toCharArray());
119 | System.out.println(user1.equals(user2));
120 | System.out.println(user1.hashCode() == user2.hashCode());
121 | System.out.println(user1);
122 |
123 |
124 | // ## Record implementation
125 |
126 | // For a record, the methods `equals()`/`hashCode()` and `toString()` are already provided
127 | // so usually you don't have to provide a new implementation.
128 | record User(String name, int age) {
129 | public User {
130 | Objects.requireNonNull(name);
131 | }
132 | // the compiler automatically adds equals/hashCode/toString !
133 | }
134 | var user1 = new User("Bob", 31);
135 | var user2 = new User("Bob", 31);
136 | System.out.println(user1.equals(user2));
137 | System.out.println(user1.hashCode() == user2.hashCode());
138 | System.out.println(user1);
139 |
--------------------------------------------------------------------------------
/chapter13-contract.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Contract
6 |
7 | public enum UnitKind {
8 | MARINE(10), FLAME_THROWER(8)
9 | ;
10 | private final int maxPower;
11 |
12 | private UnitKind(int maxPower) {
13 | this.maxPower = maxPower;
14 | }
15 | }
16 |
17 | public class MilitaryUnit {
18 | private final UnitKind kind;
19 | private final int power;
20 | private IntUnaryOperator bonus;
21 |
22 | public MilitaryUnit(UnitKind kind, int power) {
23 | this.kind = Objects.requireNonNull(kind);
24 | if (power < 0 || power >= kind.maxPower) {
25 | throw new IllegalArgumentException("invalid power " + power);
26 | }
27 | this.power = power;
28 | this.bonus = x -> x;
29 | }
30 |
31 | public void bonus(IntUnaryOperator bonus) {
32 | this.bonus = Objects.requireNonNull(bonus);
33 | }
34 |
35 | public int fightingPower() {
36 | return Math.max(0, Math.min(unit.maxPower, bonus.applyAsInt(power)));
37 | }
38 | }
--------------------------------------------------------------------------------
/chapter13-modifiable_vs_mutable.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 |
--------------------------------------------------------------------------------
/chapter14-null_and_optional.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Null and Optional
6 |
7 | // In Java, any reference can be null which usually means that there is no object.
8 | // Trying to call a method or a field on null throws a `NullPointerException`.
9 | // It's not rare for a program written by a beginner to randomly stop due to a
10 | // `NullPointerException`.
11 |
12 | // Conceptually, there are two ways to avoid a null to be dereferenced,
13 | // either protect against null each time you read a value, or each time you write a value
14 | // By example, with
15 | record Person(String name) {
16 | public String toString() {
17 | return "hello " + name.toString();
18 | }
19 | }
20 |
21 | // You get an exception if you write
22 | System.out.println(new Person(null));
23 |
24 | // A stupid idea is to try to guard against all reads
25 | record Person(String name) {
26 | public String toString() {
27 | if (name == null) {
28 | return "hello unnamed";
29 | }
30 | return "hello " + name.toString();
31 | }
32 | }
33 | System.out.println(new Person(null));
34 |
35 | // It forces you to define a meaning of a name being null.
36 |
37 | // It's easier to refuse to create a Person with null as name.
38 | record Person(String name) {
39 | public Person { // it's a compact constructor
40 | Objects.requireNonNull(name);
41 | }
42 | public String toString() {
43 | return "hello " + name.toString();
44 | }
45 | }
46 | new Person(null);
47 |
48 | // You may think that we have trade a `NullPointerException` to a `NullPointerException`.
49 | // But there is a big difference, the former code was throwing a `NullPointerException`
50 | // only when toString() is called, so depending on how the class `Person` was used,
51 | // a `NullPointerException` is thrown __randomly__.
52 | // The latter code throws a `NullPointerException` if you dare to try to create
53 | // a `Person` with a name null so a `NullPointerException` is thrown __consistently__.
54 |
55 | // So in Java, the idea to avoid the spurious is `NullPointerException` to never let
56 | // a user code to get a null
57 | // - do not allow to create an object with null fields
58 | // - never return null from a method (use an Optional or an empty collection instead)
59 |
60 |
61 | // ## Defensive programming
62 |
63 | // The best way to not store null in a field (or a record component) is to reject any attempt
64 | // to call a public method with null as argument. So any public methods should call
65 | // `Objects.requireNonNull()` on all their arguments that are references.
66 | record Animal(String kind, boolean wild) {
67 | public Animal {
68 | Objects.requireNonNull(kind);
69 | // no need to do a requireNonNull on 'wild', a boolean can not be null
70 | }
71 | public boolean isDangerousWith(Animal animal) {
72 | Objects.requireNonNull(animal);
73 | return wild || !kind.equals(animal.kind);
74 | }
75 | }
76 | new Animal(null, true);
77 |
78 | // ### Map.get()
79 |
80 | // You may sometimes want to pass null to a public method or return null from a method
81 | // but it should be an exceptional case and it should be documented
82 |
83 | // A good example is `Map.get(key)` that is used a lot and is specified to return `null`
84 | // if the key is not stored in the map. refer to use `Map.getOrDefault()` instead
85 | var map = Map.of("John", 5, "Paul", 7);
86 | System.out.println(map.get("Lena"));
87 | System.out.println(map.getOrDefault("Lena", 0));
88 |
89 | // See chapter 'list and map' for more information.
90 |
91 |
92 | // ## Optional
93 | // Optional is a special class which means that a return value of a method may not be present.
94 | // Unlike a usual object type that can be null, an Optional can be present or empty.
95 | // It forces the user code be prepared to handle an empty Optional.
96 |
97 | // In the following code a `Car` has a color and optionally has a driver
98 | public class Car {
99 | private final Person driver; // may be null
100 | private final String color;
101 | public Car(String color, Person driver) {
102 | this.color = Objects.requireNonNull(color);
103 | this.driver = driver; // may be null
104 | }
105 | public String color() {
106 | return color;
107 | }
108 | public Optional driver() {
109 | return Optional.ofNullable(driver);
110 | }
111 | }
112 |
113 | // Trying to call a method of `Person` on an `Optional`, obviously doesn't work
114 | var car = new Car("red", null);
115 | var name = car.driver().name(); // doesn't compile
116 |
117 |
118 | // so the user code as to be changed to handle `Optional`, and the fact that
119 | // an `Optional` can be empty
120 | var car = new Car("red", null);
121 | var name = car.driver().map(Person::name).orElse("autopilot");
122 | System.out.println(name);
123 |
124 | // > Don't use Optional for anything else than a return type
125 | // > Never store null in an Optional because it defeats its purpose.
126 |
127 |
128 |
--------------------------------------------------------------------------------
/chapter16-exception.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Exception
6 |
7 | // In Java, an exception is a mechanism to signal that the
8 | // execution of a method can not be performed
9 | // by example, trying to get a value of a list with an index equals to -1
10 | String valueAt(List list, int index) {
11 | return list.get(index);
12 | }
13 | valueAt(List.of("hello"), -1);
14 |
15 | // an exception has
16 | // - a type that indicate the kind of error
17 | // - an error message that explain in English the issue
18 | // - a stacktrace which indicates where the exception was raised
19 | // and the methods to reach that point
20 |
21 | // In our example, `java.lang.IndexOutOfBoundsException` is the type,
22 | // `Index: -1 Size: 1` is the message and
23 | // ```
24 | // at ImmutableCollections$AbstractImmutableList.outOfBounds (ImmutableCollections.java:201)
25 | // at ImmutableCollections$List12.get (ImmutableCollections.java:418)
26 | // at valueAt (#1:2)
27 | // at (#2:1)
28 | // ```
29 | // is the stacktrace
30 |
31 |
32 | // ## `throw`
33 | // You can create (with `new`) and raise your own exception using the keyword `throw`
34 | String valueAt(List list, int index) {
35 | if (index < 0 || index >= list.size()) {
36 | throw new IllegalArgumentException("invalid index " + index);
37 | }
38 | return list.get(index);
39 | }
40 | valueAt(List.of("hello"), -1);
41 |
42 | // The stacktrace is populated automatically when you create the exception
43 | // not where you throw it so it's a good idea to create the exception
44 | // not too far from where you throw it.
45 | // In the following example, the stacktrace will say that the exception
46 | // is created at `notTooFar (#5:2)`, on the second line, not at `notTooFar (#5:4)`.
47 | void notTooFar() {
48 | var exception = new RuntimeException("i'm created here");
49 | // an empty line
50 | throw exception;
51 | }
52 | notTooFar();
53 |
54 |
55 | // ## Use existing exceptions
56 | // While you can create your own exception (see below),
57 | // usually we are re-using already existing exceptions.
58 |
59 | // Exceptions commonly used in Java
60 | // - NullPointerException if a reference is null
61 | // - IllegalArgumentException if an argument of a method is not valid
62 | // - IllegalStateException if the object state doesn't allow to proceed,
63 | // by example if a file is closed, you can not read it
64 | // - AssertionError if a code that should not be reached has been reached
65 |
66 | // By example
67 | enum State { OK, NOT_OK }
68 | void testState(State state) {
69 | switch(state) {
70 | case OK -> System.out.println("Cool !");
71 | case NOT_OK -> System.out.println("Not cool");
72 | default -> { throw new AssertionError("Danger, Will Robinson"); }
73 | }
74 | }
75 | // here the AssertionError can only be thrown if the code if testState()
76 | // and the enum State disagree on set of possible values
77 | // By example, if a new state is added
78 | enum State { OK, NOT_OK, UNKNOWN }
79 | testState(State.UNKNOWN);
80 |
81 |
82 | // ## Recovering from an exception
83 | // In Java, you can recover from an exception using a `try/catch` block.
84 | URI uri;
85 | try {
86 | uri = new URI("http://i'm a malformed uri");
87 | } catch(URISyntaxException e) {
88 | // if the URI is malformed, used google by default
89 | uri = new URI("http://www.google.fr");
90 | }
91 | System.out.println(uri);
92 |
93 | // A common mistake is to write a `try/catch` in a method with an empty catch
94 | // or a catch that log/print a message instead of actually recovering from the
95 | // exception
96 |
97 | // As a rule of thumb, if you can not write something meaningful in the catch
98 | // block then you should not use a `try/catch`.
99 |
100 |
101 | // ## Fighting with the compiler
102 | // For the compiler, there are two kinds of exceptions that are handled differently
103 | // - unchecked exception, you can throw them anywhere you want
104 | // - checked exception, you can only throw them if
105 | // - you are inside a method that declare to throws that exception (or a supertype)
106 | // - you are inside a try/catch block on that exception (or a supertype)
107 |
108 | // In Java, an exception that inherits from `RuntimeException` or `Error` are
109 | // unchecked exceptions, all the others are checked exceptions
110 |
111 | // so this code doesn't compile because `IOException` inherits from `Exception`
112 | // and not `RuntimeException`.
113 | /*
114 | void hello() {
115 | Files.delete(Path.of("I don't exist"));
116 | }
117 | */
118 |
119 | // A way to fix the issue is to use the keywords `throws` to ask the caller
120 | // of the method to deal with the exception, again the caller will have,
121 | // either by propagating it with a `throws` or recover from it with a `try/catch`.
122 | void hello() throws IOException {
123 | Files.delete(Path.of("I don't exist"));
124 | }
125 |
126 | // As a rule of thumb, 99% of the time you want to propagate the exception,
127 | // and keep the number of `try/catch` as low as possible in your program,
128 | // so prefer `throws` to `try/catch`.
129 |
130 |
131 | // ### When you can not use `throws`, wrap the exception
132 |
133 | // If a method has it's signature fixed because it overrides a method of an interface,
134 | // then you can not use `throws`
135 |
136 | // The following example doesn't compile because the method `run` of a `Runnable`
137 | // doesn't declare to `throws` `IOException` so the only solution seems to be
138 | // to use a `try/catch`.
139 | /*
140 | var aRunnable = new Runnable() {
141 | public void run() {
142 | Files.delete(Path.of("I don't exist"));
143 | }
144 | };
145 | */
146 |
147 | // So here, we have to use a `try/catch` but we still want to propagate the exception.
148 | // The trick is wrap the checked exception into an unchecked exception.
149 | // This trick is so common that the Java API already comes with existing
150 | // classes to wrap common checked exceptions. For `IOException`, the unchecked
151 | // equivalent is `UncheckedIOException`.
152 |
153 | var aRunnable = new Runnable() {
154 | public void run() {
155 | try {
156 | Files.delete(Path.of("I don't exist"));
157 | } catch(IOException e) {
158 | // the way to recover, is to propagate it as an unchecked
159 | throw new UncheckedIOException(e);
160 | }
161 | }
162 | };
163 | aRunnable.run();
164 |
165 | // The exception `UndeclaredThrowableException` is used as the generic unchecked exception
166 | // to wrap any checked exception which do not have an unchecked equivalent.
167 |
168 |
169 | // ## Create your own Exception
170 |
171 | // You can create your own exception by creating a class that inherits from `RuntimeException`
172 | // You should provide at least two constructors, one with a message and one with a message
173 | // and a cause.
174 |
175 | public class MyException extends RuntimeException {
176 | public MyException(String message) {
177 | super(message);
178 | }
179 | public MyException(String message, Throwable cause) {
180 | super(message, cause);
181 | }
182 | }
183 | throw new MyException("This is my exception");
184 |
185 | // But in general, don't ! Reuse existing commonly used exceptions.
186 |
187 |
--------------------------------------------------------------------------------
/chapter17-enum.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Enum
6 | // An enum is a kind of class where all instances are known and can be enumerated
7 |
8 | // By example, for a program they may be 3 ways to list files of a directory,
9 | // either all files (ALL), either only the normal file (NORMAL) or only the directory (DIRECTORY)
10 | enum FileListMode { ALL, NORMAL, DIRECTORY }
11 |
12 |
13 | // ## Enum instances
14 | // The enumerated instances are considered as constants thus can be accessed like any constant
15 | System.out.println(FileListMode.ALL);
16 |
17 | // All enums inherits from the class `java.lang.Enum` that defines two components
18 | // - name which is the name of the instance
19 | // - ordinal which is the index (starting at 0)
20 | System.out.println(FileListMode.ALL.name());
21 | System.out.println(FileListMode.ALL.ordinal());
22 |
23 | // equals()/hashCode() and toString() are inherited from `java.lang.Enum`
24 | // - equals() delegates to ==
25 | // - hashCode() returns ordinal
26 | // - toString() returns name
27 | System.out.println(FileListMode.ALL.equals(FileListMode.NORMAL));
28 | System.out.println(FileListMode.NORMAL);
29 | System.out.println(FileListMode.DIRECTORY.hashCode());
30 |
31 | // Enum instances are comparable (their ordinal value is used) so
32 | // ALL < NORMAL < DIRECTORY
33 | System.out.println(FileListMode.ALL.compareTo(FileListMode.NORMAL) < 0);
34 |
35 | // Two supplementary static methods are generated by the compiler
36 | // - values() return an array of all instances
37 | // - valueOf(name) return the instance corresponding to the name or an exception
38 | System.out.println(Arrays.toString(FileListMode.values()));
39 | System.out.println(FileListMode.valueOf("ALL"));
40 | System.out.println(FileListMode.valueOf("invalid"));
41 |
42 | // `values()` returns a new cloned array at each invocation
43 | // so don't call it inside a loop :)
44 |
45 |
46 | // ## Enum are classes
47 | // Unlike in C where enums are integers, enum in Java are full objects
48 | // so they can have fields, constructors and methods defined after a semicolon
49 | // at the end of the list of the instances
50 | enum FileListMode {
51 | ALL,
52 | NORMAL,
53 | DIRECTORY, // trailing comma is allowed
54 | ; // ends of the instances
55 | public String shortName() {
56 | return name().toLowerCase().substring(0, 3);
57 | }
58 | }
59 | System.out.println(FileListMode.NORMAL.shortName());
60 | System.out.println(FileListMode.DIRECTORY.shortName());
61 |
62 |
63 | // ### Enum constructors
64 | // You can add fields if you want to associate specific values to the enum instances
65 | // By example to convert from bits of an int to a set of modifier.
66 | enum Modifier {
67 | PUBLIC(1), FINAL(2), STATIC(4)
68 | ;
69 | private final int value;
70 | private Modifier(int value) {
71 | this.value = value;
72 | }
73 | // avoid to calls values() several times
74 | private static final List MODIFIERS = List.of(values());
75 | static int modifiersAsInt(Modifier... modifiers) {
76 | return Arrays.stream(modifiers).map(m -> m.value).reduce(0, (a, b) -> a | b);
77 | }
78 | static Set intAsModifierSet(int modifiers) {
79 | return MODIFIERS.stream().filter(m -> (modifiers & m.value) != 0).collect(Collectors.toSet());
80 | }
81 | }
82 | var modifiers = Modifier.modifiersAsInt(Modifier.PUBLIC, Modifier.STATIC);
83 | System.out.println("int: " + modifiers);
84 | var modifierSet = Modifier.intAsModifierSet(modifiers);
85 | System.out.println("set: " + modifierSet);
86 |
87 | // The implementation of `intAsModifierSet` can be a little more efficient, see below
88 |
89 |
90 | // ## Enum with abstract methods
91 | // An enum can have abstract methods, in that case, all instances have to implement the missing method bodies
92 | // using the same syntax as the anonymous class one
93 | // In that case, the compiler generates one anonymous class per enum instance.
94 | interface FilePredicate {
95 | boolean test(Path path) throws IOException;
96 | }
97 | enum FileListMode implements FilePredicate {
98 | ALL {
99 | public boolean test(Path path) throws IOException {
100 | return true;
101 | }
102 | },
103 | NORMAL {
104 | public boolean test(Path path) throws IOException {
105 | return !Files.isHidden(path);
106 | }
107 | },
108 | DIRECTORY {
109 | public boolean test(Path path) throws IOException {
110 | return NORMAL.test(path) && Files.isDirectory(path);
111 | }
112 | }
113 | }
114 |
115 | // It can be used to list the files of a directory in a way that
116 | // depend on the mode. If you don't understand the cast in the for loop
117 | // see chapter 'iteration'
118 | void printAllPath(Path directory, FileListMode mode) throws IOException {
119 | try(var stream = Files.list(directory)) {
120 | for(var path: (Iterable)stream::iterator) {
121 | if (mode.test(path)) {
122 | System.out.println(path);
123 | }
124 | }
125 | }
126 | }
127 | printAllPath(Path.of("."), FileListMode.DIRECTORY);
128 |
129 |
130 | // ### Use delegation, not inheritance
131 | // The implementation above uses inheritance where it should use delegation
132 | // Here is a better implementation delegating each implementation to a lambda.
133 | enum FileListMode {
134 | ALL(path -> true),
135 | NORMAL(path -> !Files.isHidden(path)),
136 | DIRECTORY(path -> NORMAL.test(path) && Files.isDirectory(path))
137 | ;
138 | private final FilePredicate predicate;
139 | FileListMode(FilePredicate predicate) {
140 | this.predicate = predicate;
141 | }
142 | public boolean test(Path path) throws IOException {
143 | return predicate.test(path);
144 | }
145 | }
146 | printAllPath(Path.of("."), FileListMode.DIRECTORY);
147 |
148 |
149 | // ## EnumSet and EnumMap
150 | // There are one implementations of set (respectively map) specific if all values
151 | // comes from the same enum because in that case ordinal() is a perfect hash function
152 |
153 | // so a EnumSet is implemented
154 | // - using only one long if there is less than 64 enum instances
155 | // - using an array of longs if there are more instances
156 | // because there are two implementations, you have to use factory methods
157 | // that takes the enum class to get an instance of the set
158 | var emptySet = EnumSet.noneOf(Modifier.class);
159 | var enumSet = EnumSet.of(Modifier.PUBLIC, Modifier.FINAL);
160 | System.out.println(enumSet);
161 |
162 | // and EnumMap is implemented as an array of values, the index being the value of ordinal()
163 | var enumMap = new EnumMap<>(Map.of(Modifier.PUBLIC, "private", Modifier.FINAL, "final"));
164 | System.out.println(enumMap);
165 |
166 |
--------------------------------------------------------------------------------
/chapter21-wrapper.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Wrapper types
6 | // Primitive types are not objects, but all data structures in Java (list, map, etc)
7 | // are storing objects, so we need a mechanism to see all primitive values as objects.
8 |
9 | // For that, the Java API defines the for each primitive type, a wrapper class,
10 | // in the package java.lang which is a class with one field
11 | // and the compiler as a mechanism named auto-boxing/auto-unboxing to convert
12 | // from a primitive type to the corresponding wrapper and vice-versa.
13 |
14 | // By example, to be able to type the list, we need an object type corresponding
15 | // to the primitive type int
16 | List list = List.of(1, 2, 3);
17 |
18 |
19 | // ## Auto-boxing
20 | // You can do the conversion explicitly calling the static method `valueOf` on the wrapper type
21 | // or implicitly if the target type is Object
22 |
23 | // ### boolean -> java.lang.Boolean
24 | Object booleanBox = Boolean.valueOf(true);
25 | Object booleanBox = true;
26 | System.out.println(booleanBox);
27 |
28 | // ### char -> java.lang.Character
29 | Object charBox = Character.valueOf('A');
30 | Object charBox = 'A';
31 | System.out.println(charBox);
32 |
33 | // ### byte -> java.lang.Byte
34 | Object byteBox = Byte.valueOf((byte) 12);
35 | Object byteBox = (byte) 12;
36 | System.out.println(byteBox);
37 |
38 | // ### short -> java.lang.Short
39 | Object shortBox = Short.valueOf((short) 712);
40 | Object shortBox = (short) 712;
41 | System.out.println(shortBox);
42 |
43 | // ### int -> java.lang.Integer
44 | Object intBox = Integer.valueOf(100_000);
45 | Object intBox = 100_000;
46 | System.out.println(intBox);
47 |
48 | // ### long -> java.lang.Long
49 | Object longBox = Long.valueOf(10_000_000_000L);
50 | Object longBox = 10_000_000_000L;
51 | System.out.println(longBox);
52 |
53 | // ### float -> java.lang.Float
54 | Object floatBox = Float.valueOf(1.2f);
55 | Object floatBox = 1.2f;
56 | System.out.println(floatBox);
57 |
58 | // ### double -> java.lang.Double
59 | Object doubleBox = Double.valueOf(42.0);
60 | Object doubleBox = 42.0;
61 | System.out.println(doubleBox);
62 |
63 |
64 | // ## DON'T USE == on a wrapper, use equals()
65 | // Because the wrapper are objects, the == will test the reference, not the value.
66 |
67 | // In fact, it's worst than that, the methods `valueOf()` can use a cache,
68 | // so depending on the weather (the startup parameters of the VM),
69 | // `valueOf` may return the same object for the same value or not
70 | // so you can not predict if == will returns true of false for a wrapper.
71 | // __Never use == on a wrapper, use equals() __
72 |
73 | Object intBox1 = 8192;
74 | Object intBox2 = 8192;
75 | System.out.println(intBox1 == intBox2); // may return true or false ??
76 |
77 |
78 | // ## Floating points
79 | // equals() on Float and Double works correctly with NaN,
80 | // unlike the primitive types float and double.
81 | double d = Double.NaN;
82 | System.out.println(d == d);
83 | Double d = Double.NaN;
84 | System.out.println(d.equals(d));
85 |
86 |
87 | // ## Auto-unboxing
88 | //
89 | // so reusing the variable `*box` defined above, we can write
90 |
91 | // ### java.lang.Boolean -> boolean
92 | boolean z = ((Boolean) booleanBox).booleanValue();
93 | boolean z = (boolean) booleanBox;
94 | System.out.println(z);
95 |
96 | // ### java.lang.Character -> char
97 | char c = ((Character) charBox).charValue();
98 | char c = (char) charBox;
99 | System.out.println(c);
100 |
101 | // ### java.lang.Byte -> byte
102 | byte b = ((Byte) byteBox).byteValue();
103 | byte b = (byte) byteBox;
104 | System.out.println(b);
105 |
106 | // ### java.lang.Short -> short
107 | short s = ((Short) shortBox).shortValue();
108 | short s = (short) shortBox;
109 | System.out.println(s);
110 |
111 | // ### java.lang.Integer -> int
112 | int i = ((Integer) intBox).intValue();
113 | int i = (int) intBox;
114 | System.out.println(i);
115 |
116 | // ### java.lang.Long -> long
117 | long l = ((Long) longBox).longValue();
118 | long l = (long) longBox;
119 | System.out.println(l);
120 |
121 | // ### java.lang.Float -> float
122 | float f = ((Float) floatBox).floatValue();
123 | float f = (float) floatBox;
124 | System.out.println(f);
125 |
126 | // ### java.lang.Double -> double
127 | double d = ((Double) doubleBox).doubleValue();
128 | double d = (double) doubleBox;
129 | System.out.println(d);
130 |
131 |
132 | // ## Beware of null
133 | // Trying to unbox null throws a NullPointerException
134 | Integer box = null;
135 | int i = (int) box;
136 |
137 |
138 | // ## Where not to use a wrapper type
139 | // Wrapper types should only appears in between '<' and '>'
140 | // because the generics of Java doesn't support primitive type inside the '<' and '>'.
141 |
142 | // So please don't use them as type of a field or a method parameter
143 | // unless you have a very good reason.
144 |
--------------------------------------------------------------------------------
/chapter22-variance.jsh:
--------------------------------------------------------------------------------
1 | // To starts, run jshell --enable-preview which is a program able to interpret Java syntax
2 | // then cut and paste the following lines to see how it works
3 | // To exit jshell type /exit
4 |
5 | // # Generics Variance
6 | // Let say we have a list of String and a list of Integer and
7 | // we want to write a method being able to print all the values
8 | // from those lists, we may write something like this
9 | void printAll(List