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