├── .github ├── dependabot.yml └── workflows │ └── gradle.yml ├── .gitignore ├── README.md ├── build.gradle ├── client ├── build.gradle └── src │ └── main │ └── java │ ├── com │ └── kousenit │ │ └── clients │ │ └── Main.java │ └── module-info.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── output.txt ├── server ├── build.gradle └── src │ ├── main │ └── java │ │ ├── com │ │ └── oreilly │ │ │ └── suppliers │ │ │ └── NamesSupplier.java │ │ └── module-info.java │ └── test │ └── java │ └── com │ └── oreilly │ └── suppliers │ └── NamesSupplierTest.java ├── settings.gradle └── src ├── main ├── java │ ├── com │ │ └── kousenit │ │ │ └── recipes │ │ │ ├── DiamondOperator.java │ │ │ ├── collections │ │ │ ├── Holder.java │ │ │ └── MapDemo.java │ │ │ ├── collectors │ │ │ ├── Department.java │ │ │ ├── Developer.java │ │ │ ├── Employee.java │ │ │ ├── GroupByDepartment.java │ │ │ ├── GroupByTasks.java │ │ │ └── Task.java │ │ │ ├── datetime │ │ │ └── DateRange.java │ │ │ ├── generics │ │ │ └── SafeVaragsDemo.java │ │ │ ├── http │ │ │ ├── AstroClient.java │ │ │ ├── JokeClient.java │ │ │ └── JokeResponse.java │ │ │ ├── interfaces │ │ │ ├── PrivateDemo.java │ │ │ └── SumNumbers.java │ │ │ ├── optional │ │ │ ├── Customer.java │ │ │ ├── CustomerDAO.java │ │ │ └── UseCustomerDAO.java │ │ │ ├── processes │ │ │ └── ProcessDemo.java │ │ │ ├── streams │ │ │ ├── RunDemo.java │ │ │ └── TakeWhileDemo.java │ │ │ └── walker │ │ │ └── StackWalkerDemo.java │ └── module-info.java └── resources │ └── myfile.txt └── test └── java └── com └── kousenit └── recipes ├── collections ├── ImmutableCollectionsTest.java └── ImmutableMapTest.java ├── datetime └── DateRangeTest.java ├── generics └── SafeVaragsDemoTest.java ├── http ├── AstroClientTest.java └── JokeClientTest.java ├── interfaces ├── PrivateDemoTest.java └── SumNumbersTest.java ├── lvti └── VarTypeTest.java └── streams └── StreamTests.java /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: org.junit.jupiter:junit-jupiter 11 | versions: 12 | - 5.7.1 13 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Set up JDK 11 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 11 16 | - name: Build with Gradle 17 | run: ./gradlew build 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | bin 3 | classes 4 | .idea 5 | .gradle 6 | .project 7 | .classpath 8 | .settings 9 | out 10 | *.iml 11 | *.ipr 12 | *.iws 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java_9_recipes 2 | Source code for Java 9 chapter of Modern Java Recipes 3 | 4 | com.kousenit.recipes.http://shop.oreilly.com/product/0636920056669.do 5 | 6 | Note: As of Dec 2018, tests have been ported to JUnit 5, modules have been added (Gson and java.logging) along with the associated Gradle modifications, and 7 | the code builds properly on Java 11.0.2-open. 8 | 9 | Ken Kousen 10 | 11 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'idea' 4 | id 'eclipse' 5 | } 6 | 7 | ext.libs = [ 8 | gson: 'com.google.code.gson:gson:2.13.0', 9 | hamcrest: 'org.hamcrest:hamcrest:3.0', 10 | junit_api: 'org.junit.jupiter:junit-jupiter:5.13.0', 11 | ] 12 | 13 | allprojects { 14 | group 'com.kousenit' 15 | version '1.0' 16 | 17 | sourceCompatibility = javaVersion 18 | targetCompatibility = javaVersion 19 | 20 | repositories { 21 | jcenter() 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation libs.gson 27 | testImplementation libs.junit_api 28 | testImplementation libs.hamcrest 29 | } 30 | 31 | test { 32 | useJUnitPlatform() 33 | } 34 | 35 | compileJava { 36 | doFirst { 37 | options.compilerArgs = [ 38 | '--module-path', classpath.asPath, 39 | ] 40 | classpath = files() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'application' 4 | } 5 | 6 | mainClassName = 'com.kousenit.clients.Main' 7 | 8 | dependencies { 9 | implementation project(':server') 10 | testImplementation libs.junit_api 11 | testImplementation libs.hamcrest 12 | } 13 | 14 | test { 15 | useJUnitPlatform() 16 | } 17 | 18 | compileJava { 19 | doFirst { 20 | options.compilerArgs = [ 21 | '--module-path', classpath.asPath, 22 | ] 23 | classpath = files() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/src/main/java/com/kousenit/clients/Main.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.clients; 2 | 3 | import com.oreilly.suppliers.NamesSupplier; 4 | 5 | import java.util.stream.Stream; 6 | import java.util.logging.*; 7 | 8 | public class Main { 9 | public static void main(String[] args) { 10 | Logger logger = Logger.getLogger(Main.class.getName()); 11 | 12 | NamesSupplier supplier = new NamesSupplier(); 13 | 14 | try (Stream lines = supplier.get()) { 15 | lines.forEach(line -> logger.info(String.format("Hello, %s!%n", line))); 16 | } 17 | try (Stream lines = supplier.get()) { 18 | lines.forEach(line -> System.out.printf("What up, %s?%n", line)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module com.kousenit.clients { 2 | requires java.logging; 3 | requires com.oreilly.suppliers; 4 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | javaVersion=11 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kousen/java_9_recipes/ea3ea6f485b2b1fe8e165b30b6ed555b7540d61f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /output.txt: -------------------------------------------------------------------------------- 1 | README.md 2 | bin 3 | build 4 | build.gradle 5 | client 6 | gradle 7 | gradle.properties 8 | gradlew 9 | gradlew.bat 10 | out 11 | output.txt 12 | server 13 | settings.gradle 14 | src 15 | -------------------------------------------------------------------------------- /server/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | dependencies { 6 | implementation libs.gson 7 | testImplementation libs.junit_api 8 | testImplementation libs.hamcrest 9 | } 10 | 11 | test { 12 | useJUnitPlatform() 13 | } 14 | 15 | compileJava { 16 | doFirst { 17 | options.compilerArgs = [ 18 | '--module-path', classpath.asPath, 19 | ] 20 | classpath = files() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/src/main/java/com/oreilly/suppliers/NamesSupplier.java: -------------------------------------------------------------------------------- 1 | package com.oreilly.suppliers; 2 | 3 | import com.google.gson.Gson; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.function.Supplier; 8 | import java.util.stream.Stream; 9 | 10 | public class NamesSupplier implements Supplier> { 11 | private List strings = Arrays.asList("Londo", "Vir", "G'Kar", "Na'Toth", 12 | "Delenn", "Lennier", "Kosh"); 13 | 14 | private Gson gson; 15 | 16 | public List getStrings() { 17 | return strings; 18 | } 19 | 20 | @Override 21 | public Stream get() { 22 | return strings.stream(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module com.oreilly.suppliers { 2 | requires com.google.gson; 3 | exports com.oreilly.suppliers; 4 | } -------------------------------------------------------------------------------- /server/src/test/java/com/oreilly/suppliers/NamesSupplierTest.java: -------------------------------------------------------------------------------- 1 | package com.oreilly.suppliers; 2 | 3 | 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assertions.*; 7 | 8 | public class NamesSupplierTest { 9 | private NamesSupplier supplier = new NamesSupplier(); 10 | 11 | @Test 12 | public void get() { 13 | supplier.get() 14 | .forEach(line -> assertTrue(supplier.getStrings().contains(line))); 15 | } 16 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'java_9_recipes' 2 | include 'client' 3 | include 'server' 4 | 5 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/DiamondOperator.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.function.Consumer; 6 | 7 | public class DiamondOperator { 8 | public static void main(String[] args) { 9 | List strings = new ArrayList<>(); 10 | 11 | Consumer consumer = new Consumer<>() { 12 | @Override 13 | public void accept(String s) { 14 | System.out.println("Hello, " + s); 15 | } 16 | }; 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/collections/Holder.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.collections; 2 | 3 | public class Holder { 4 | private int x; 5 | 6 | public Holder(int x) { this.x = x; } 7 | 8 | public void setX(int x) { 9 | this.x = x; 10 | } 11 | 12 | public int getX() { 13 | return x; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/collections/MapDemo.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.collections; 2 | 3 | import java.util.Map; 4 | 5 | import static java.util.Map.entry; 6 | 7 | public class MapDemo { 8 | public static void main(String[] args) { 9 | Map map = Map.ofEntries(entry("a", 1), 10 | entry("b", 2), 11 | entry("c", 3)); 12 | map.forEach((k,v) -> System.out.println(k + " : " + v)); 13 | System.out.println(map.getClass().getName()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/collectors/Department.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.collectors; 2 | 3 | public class Department { 4 | private String name; 5 | 6 | public Department(String name) { 7 | this.name = name; 8 | } 9 | 10 | public String getName() { 11 | return name; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/collectors/Developer.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.collectors; 2 | 3 | public class Developer { 4 | private String name; 5 | 6 | public Developer(String name) { 7 | this.name = name; 8 | } 9 | 10 | public String getName() { 11 | return name; 12 | } 13 | 14 | public void setName(String name) { 15 | this.name = name; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return name; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/collectors/Employee.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.collectors; 2 | 3 | public class Employee { 4 | private String name; 5 | private Department department; 6 | 7 | public Employee(String name, Department department) { 8 | this.name = name; 9 | this.department = department; 10 | } 11 | 12 | public String getName() { 13 | return name; 14 | } 15 | 16 | public Department getDepartment() { 17 | return department; 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return name; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/collectors/GroupByDepartment.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.collectors; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import static java.util.stream.Collectors.groupingBy; 8 | 9 | public class GroupByDepartment { 10 | public static void main(String[] args) { 11 | Department it = new Department("IT"); 12 | Department management = new Department("Management"); 13 | Department sales = new Department("sales"); 14 | List employees = Arrays.asList(new Employee("Venkat", it), 15 | new Employee("Raju", it), 16 | new Employee("Matt", management), 17 | new Employee("Nate", it)); 18 | 19 | Map> empMap = employees.stream() 20 | .collect(groupingBy(Employee::getDepartment)); 21 | 22 | printEmpMap("No filtering", empMap); 23 | 24 | } 25 | 26 | private static void printEmpMap(String title, Map> map) { 27 | System.out.println(); 28 | System.out.println(title); 29 | map.forEach((dept, empList) -> 30 | System.out.printf("%10s: %s%n", dept.getName(), empList)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/collectors/GroupByTasks.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.collectors; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import java.util.stream.Collectors; 8 | 9 | import static java.util.stream.Collectors.*; 10 | 11 | public class GroupByTasks { 12 | public static void main(String[] args) { 13 | Developer venkat = new Developer("Venkat"); 14 | Developer daniel = new Developer("Daniel"); 15 | Developer brian = new Developer("Brian"); 16 | Developer matt = new Developer("Matt"); 17 | Developer nate = new Developer("Nate"); 18 | Developer craig = new Developer("Craig"); 19 | Developer ken = new Developer("Ken"); 20 | 21 | Task java = new Task("Java stuff", 100); 22 | Task altJvm = new Task("Groovy/Kotlin/Scala/Clojure", 50); 23 | Task javaScript = new Task("JavaScript (sorry)", 100); 24 | Task spring = new Task("Spring", 50); 25 | Task jpa = new Task("JPA/Hibernate", 20); 26 | 27 | java.addDevelopers(venkat, daniel, brian, ken); 28 | javaScript.addDevelopers(venkat, nate); 29 | spring.addDevelopers(craig, matt, nate, ken); 30 | altJvm.addDevelopers(venkat, daniel, ken); 31 | 32 | List tasks = Arrays.asList(java, altJvm, javaScript, spring, jpa); 33 | 34 | // No filtering 35 | Map> taskMap = tasks.stream() 36 | .collect(groupingBy(Task::getBudget)); 37 | 38 | printMap("Complete map (no filtering)", taskMap); 39 | 40 | // Filter out low budget tasks 41 | taskMap = tasks.stream() 42 | .filter(task -> task.getBudget() > 25) 43 | .collect(groupingBy(Task::getBudget)); 44 | 45 | printMap("Filter out low budget tasks", taskMap); 46 | 47 | // Filter low budget tasks, but still include them in output report 48 | taskMap = tasks.stream() 49 | .collect(groupingBy(Task::getBudget, 50 | filtering(task -> task.getBudget() > 25, toList()))); 51 | 52 | printMap("filtering in Collectors", taskMap); 53 | 54 | Map> tasksByName = tasks.stream() 55 | .collect(groupingBy(Task::getName)); 56 | System.out.println(); 57 | tasksByName.forEach((name, taskList) -> System.out.printf("%30s: %s%n", name, taskList)); 58 | 59 | Map>> map = tasks.stream() 60 | .collect(groupingBy(Task::getName, Collectors.mapping(Task::getDevelopers, toSet()))); 61 | 62 | System.out.println(); 63 | map.forEach((name, setListDevs) -> System.out.printf("%30s: %s%n", name, setListDevs)); 64 | 65 | Map> task2setdevs = tasks.stream() 66 | .collect(groupingBy(Task::getName, 67 | Collectors.flatMapping(task -> task.getDevelopers().stream(), toSet()))); 68 | System.out.println(); 69 | task2setdevs.forEach((name, devSet) -> System.out.printf("%30s: %s%n", name, devSet)); 70 | 71 | } 72 | 73 | private static void printMap(String title, Map> taskMap) { 74 | System.out.println(); 75 | System.out.println(title); 76 | taskMap.forEach((budget, taskList) -> 77 | System.out.printf("%3d: %s%n", budget, taskList)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/collectors/Task.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.collectors; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | public class Task { 9 | private String name; 10 | private long budget; 11 | private List developers = new ArrayList<>(); 12 | 13 | public Task(String name, long budget) { 14 | this.name = name; 15 | this.budget = budget; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | public long getBudget() { 27 | return budget; 28 | } 29 | 30 | public void setBudget(long budget) { 31 | this.budget = budget; 32 | } 33 | 34 | public void addDevelopers(Developer... devs) { 35 | developers = Arrays.stream(devs) 36 | .collect(Collectors.toList()); 37 | } 38 | 39 | public List getDevelopers() { 40 | return developers; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return name; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/datetime/DateRange.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.datetime; 2 | 3 | import java.time.LocalDate; 4 | import java.time.Month; 5 | import java.time.Period; 6 | import java.time.temporal.ChronoUnit; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | import java.util.stream.LongStream; 10 | import java.util.stream.Stream; 11 | 12 | public class DateRange { 13 | public List getDays_java8(LocalDate start, LocalDate end) { 14 | Period period = start.until(end); 15 | // Trap! For dates with the same day, period.getDays() returns 0 (whoa) 16 | return LongStream.range(0, ChronoUnit.DAYS.between(start, end)) 17 | .mapToObj(start::plusDays) 18 | .collect(Collectors.toList()); 19 | } 20 | 21 | public List getDaysByIterate(LocalDate start, int days) { 22 | return Stream.iterate(start, date -> date.plusDays(1)) 23 | .limit(days) 24 | .collect(Collectors.toList()); 25 | } 26 | 27 | public List getDays_java9(LocalDate start, LocalDate end) { 28 | return start.datesUntil(end) 29 | .collect(Collectors.toList()); 30 | } 31 | 32 | public List getMonths_java8(LocalDate start, LocalDate end) { 33 | Period period = start.until(end); 34 | 35 | // Trap! For dates one year apart, period.getMonths() returns 0 (whoa) 36 | return LongStream.range(0, ChronoUnit.MONTHS.between(start, end)) 37 | .mapToObj(start::plusMonths) 38 | .collect(Collectors.toList()); 39 | } 40 | 41 | public List getMonths_java9(LocalDate start, LocalDate end) { 42 | return start.datesUntil(end, Period.ofMonths(1)) 43 | .collect(Collectors.toList()); 44 | } 45 | 46 | public static void main(String[] args) { 47 | DateRange dateRange = new DateRange(); 48 | LocalDate start = LocalDate.of(2017, Month.JUNE, 10); 49 | LocalDate end = LocalDate.of(2017, Month.JULY, 10); 50 | 51 | System.out.println(dateRange.getDays_java8(start, end)); 52 | System.out.println(dateRange.getDays_java9(start, end)); 53 | 54 | end = start.plusYears(1); 55 | System.out.println(dateRange.getMonths_java8(start, end)); 56 | System.out.println(dateRange.getMonths_java9(start, end)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/generics/SafeVaragsDemo.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.generics; 2 | 3 | import java.time.LocalDate; 4 | 5 | @SuppressWarnings("UnusedReturnValue") 6 | public class SafeVaragsDemo { 7 | 8 | public String[] replaceFirstStringWithDate(LocalDate date, String... strings) { 9 | strings[0] = date.toString(); 10 | return strings; 11 | } 12 | 13 | @SafeVarargs // can only be applied to final methods 14 | public final T[] replaceFirstValueInArray(T value, T... array) { 15 | // Compiles because of type erasure 16 | // But "possible heap pollution from parameterized vararg type" 17 | array[0] = value; 18 | return array; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/http/AstroClient.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.http; 2 | 3 | import java.io.IOException; 4 | import java.net.URI; 5 | import java.net.http.HttpClient; 6 | import java.net.http.HttpRequest; 7 | import java.net.http.HttpResponse; 8 | import java.time.Duration; 9 | 10 | public class AstroClient { 11 | 12 | public String getJsonResponse() { 13 | HttpClient client = HttpClient.newBuilder() 14 | .connectTimeout(Duration.ofSeconds(2)) 15 | .build(); 16 | 17 | HttpRequest request = HttpRequest.newBuilder() 18 | .uri(URI.create("http://api.open-notify.org/astros.json")) 19 | .GET() 20 | .build(); 21 | 22 | try { 23 | HttpResponse response = 24 | client.send(request, HttpResponse.BodyHandlers.ofString()); 25 | System.out.println(response.statusCode()); 26 | System.out.println(response.headers()); 27 | return response.body(); 28 | } catch (IOException | InterruptedException e) { 29 | e.printStackTrace(); 30 | } 31 | return ""; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/http/JokeClient.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.http; 2 | 3 | import java.io.IOException; 4 | import java.net.URI; 5 | import java.net.http.HttpClient; 6 | import java.net.http.HttpRequest; 7 | import java.net.http.HttpResponse; 8 | import java.time.Duration; 9 | import java.util.concurrent.ExecutionException; 10 | 11 | import com.google.gson.Gson; 12 | 13 | public class JokeClient { 14 | private HttpClient client = HttpClient.newBuilder() 15 | .version(HttpClient.Version.HTTP_2) 16 | .connectTimeout(Duration.ofSeconds(2)) 17 | .build(); 18 | private Gson gson = new Gson(); 19 | 20 | private HttpRequest buildRequest(String first, String last) { 21 | String jokeUrl = "http://api.icndb.com/jokes/random?limitTo=[nerdy]"; 22 | String url = String.format("%s&firstName=%s&lastName=%s", jokeUrl, first, last); 23 | return HttpRequest.newBuilder() 24 | .uri(URI.create(url)) 25 | .GET() 26 | .build(); 27 | } 28 | 29 | public String getJokeSync(String first, String last) throws IOException, InterruptedException { 30 | HttpResponse response = client.send( 31 | buildRequest(first, last), 32 | HttpResponse.BodyHandlers.ofString()); 33 | return getJoke(response.body()); 34 | } 35 | 36 | 37 | public String getJokeAsync(String first, String last) throws ExecutionException, InterruptedException { 38 | String json = client.sendAsync(buildRequest(first, last), 39 | HttpResponse.BodyHandlers.ofString()) 40 | .thenApply(HttpResponse::body) 41 | .get(); 42 | return getJoke(json); 43 | } 44 | 45 | private String getJoke(String jsonData) { 46 | return gson.fromJson(jsonData, JokeResponse.class) 47 | .getValue().getJoke(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/http/JokeResponse.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.http; 2 | 3 | public class JokeResponse { 4 | private String type; 5 | private Value value; 6 | 7 | public String getType() { 8 | return type; 9 | } 10 | 11 | public void setType(String type) { 12 | this.type = type; 13 | } 14 | 15 | public Value getValue() { 16 | return value; 17 | } 18 | 19 | public void setValue(Value value) { 20 | this.value = value; 21 | } 22 | 23 | public class Value { 24 | private int id; 25 | private String joke; 26 | private String[] categories; 27 | 28 | public int getId() { 29 | return id; 30 | } 31 | 32 | public void setId(int id) { 33 | this.id = id; 34 | } 35 | 36 | public String getJoke() { 37 | return joke; 38 | } 39 | 40 | public void setJoke(String joke) { 41 | this.joke = joke; 42 | } 43 | 44 | public String[] getCategories() { 45 | return categories; 46 | } 47 | 48 | public void setCategories(String[] categories) { 49 | this.categories = categories; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/interfaces/PrivateDemo.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.interfaces; 2 | 3 | public class PrivateDemo implements SumNumbers { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/interfaces/SumNumbers.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.interfaces; 2 | 3 | import java.util.function.IntPredicate; 4 | import java.util.stream.IntStream; 5 | 6 | public interface SumNumbers { 7 | IntPredicate EVENS = n -> n % 2 == 0; 8 | 9 | default int addEvens(int... nums) { 10 | // return add(n -> n % 2 == 0, nums); 11 | return add(EVENS, nums); 12 | } 13 | 14 | default int addOdds(int... nums) { 15 | return add(n -> n % 2 != 0, nums); 16 | } 17 | 18 | private int add(IntPredicate predicate, int... nums) { 19 | return IntStream.of(nums) 20 | .filter(predicate) 21 | .sum(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/optional/Customer.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.optional; 2 | 3 | public class Customer { 4 | public static final Customer DEFAULT = new Customer("Default"); 5 | private static int nextId; 6 | private final int id; 7 | private final String name; 8 | 9 | public Customer(String name) { 10 | this.id = nextId++; 11 | this.name = name; 12 | } 13 | 14 | public static Customer getDefault() { 15 | return DEFAULT; 16 | } 17 | 18 | public int getId() { 19 | return id; 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return name; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/optional/CustomerDAO.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.optional; 2 | 3 | import java.util.*; 4 | import java.util.function.Function; 5 | import java.util.stream.Collectors; 6 | 7 | public class CustomerDAO { 8 | private Map map = new HashMap<>(); 9 | 10 | public CustomerDAO() { 11 | List customers = Arrays.asList( 12 | new Customer("Londo"), 13 | new Customer("G'Kar"), 14 | new Customer("Delenn"), 15 | new Customer("Lennier"), 16 | new Customer("Kosh"), 17 | new Customer("Vir"), 18 | new Customer("Zathras")); 19 | 20 | map = customers.stream() 21 | .collect(Collectors.toMap(Customer::getId, Function.identity())); 22 | } 23 | 24 | public Optional findById(int id) { 25 | return findByIdLocal(id) 26 | .or(() -> findByIdRemote(id)) 27 | .or(() -> Optional.of(Customer.DEFAULT)); 28 | } 29 | 30 | public Collection findAllById(Integer... ids) { 31 | return Arrays.stream(ids) 32 | .map(this::findById) 33 | .filter(Optional::isPresent) 34 | .map(Optional::get) 35 | .collect(Collectors.toList()); 36 | } 37 | 38 | public Collection findAllByIdBetter(Integer... ids) { 39 | return Arrays.stream(ids) 40 | .map(this::findById) 41 | .flatMap(Optional::stream) 42 | .collect(Collectors.toList()); 43 | } 44 | 45 | public Optional findByIdRemote(int id) { 46 | return Optional.empty(); 47 | } 48 | 49 | public Optional findByIdLocal(int id) { 50 | return Optional.ofNullable(map.get(id)); 51 | } 52 | 53 | public void printCustomer(Integer id) { 54 | findByIdLocal(id).ifPresent(System.out::println); 55 | } 56 | 57 | public void printCustomerWithDefault(Integer id) { 58 | findByIdLocal(id).ifPresentOrElse(System.out::println, 59 | () -> System.out.println("Customer with id=" + id + " not found")); 60 | } 61 | 62 | public void printCustomers(Integer... ids) { 63 | Arrays.asList(ids) 64 | .forEach(this::printCustomer); 65 | } 66 | 67 | public void printCustomersWithDefaults(Integer... ids) { 68 | Arrays.asList(ids) 69 | .forEach(this::printCustomerWithDefault); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/optional/UseCustomerDAO.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.optional; 2 | 3 | import java.util.Optional; 4 | 5 | public class UseCustomerDAO { 6 | public static void main(String[] args) { 7 | CustomerDAO dao = new CustomerDAO(); 8 | 9 | System.out.println("Using Optional::isPresent and Optional::get"); 10 | dao.findAllById(1, 2, 4, 7, 11) 11 | .forEach(System.out::println); 12 | 13 | System.out.println("Using Optional::stream"); 14 | dao.findAllByIdBetter(1, 2, 4, 7, 11) 15 | .forEach(System.out::println); 16 | 17 | System.out.println("Using Optional::or"); 18 | Optional optional = dao.findById(99); 19 | Customer customer = optional.orElse(Customer.DEFAULT); 20 | System.out.println(customer); 21 | 22 | Optional opt = dao.findById(99); 23 | System.out.println(opt); 24 | 25 | System.out.println("Using Optional::ifPresentOrElse"); 26 | dao.printCustomers(1, 2, 4, 7, 11); 27 | dao.printCustomersWithDefaults(1, 2, 4, 7, 11); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/processes/ProcessDemo.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.processes; 2 | 3 | import java.io.File; 4 | 5 | public class ProcessDemo { 6 | public static void main(String[] args) throws Exception { 7 | ProcessBuilder builder = new ProcessBuilder("ls"); 8 | builder.redirectOutput(new File("output.txt")); 9 | Process proc = builder.start(); 10 | 11 | System.out.println(proc.isAlive()); 12 | System.out.println(proc.pid()); 13 | 14 | Process proc1 = Runtime.getRuntime().exec("ls"); 15 | System.out.println(proc1.isAlive()); 16 | System.out.println(proc1.pid()); 17 | proc1.destroy(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/streams/RunDemo.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.streams; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | 6 | public class RunDemo { 7 | public static void main(String[] args) { 8 | ExecutorService service = Executors.newCachedThreadPool(); 9 | 10 | TakeWhileDemo demo = new TakeWhileDemo(); 11 | service.execute(demo); 12 | service.execute(demo::stop); 13 | 14 | service.shutdown(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/streams/TakeWhileDemo.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.streams; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.util.concurrent.atomic.AtomicBoolean; 7 | import java.util.stream.Stream; 8 | 9 | public class TakeWhileDemo implements Runnable { 10 | private AtomicBoolean running = new AtomicBoolean(true); 11 | 12 | @Override 13 | public void run() { 14 | try (BufferedReader reader = 15 | new BufferedReader(new InputStreamReader(System.in)); 16 | Stream lines = reader.lines()) { 17 | System.out.println("Running..."); 18 | //lines.takeWhile(s -> running.get()) 19 | lines 20 | .takeWhile(s -> running.get()) 21 | .forEach(s -> System.out.println(running.get() + " " + s)); 22 | } catch (IOException e) { 23 | e.printStackTrace(); 24 | } 25 | } 26 | 27 | public void stop() { 28 | running.set(false); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/kousenit/recipes/walker/StackWalkerDemo.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.walker; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | 6 | public class StackWalkerDemo { 7 | public static void main(String[] args) { 8 | List frames = StackWalker.getInstance() 9 | .walk(s -> s.limit(10) 10 | .collect(Collectors.toList())); 11 | frames.forEach(System.out::println); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module com.kousenit.recipes { 2 | // Java 10: 3 | // requires jdk.incubator.httpclient; 4 | 5 | // Java 11: 6 | requires java.net.http; 7 | 8 | requires java.logging; 9 | requires com.google.gson; 10 | } -------------------------------------------------------------------------------- /src/main/resources/myfile.txt: -------------------------------------------------------------------------------- 1 | This is a file 2 | with multiple lines 3 | for demo purposes -------------------------------------------------------------------------------- /src/test/java/com/kousenit/recipes/collections/ImmutableCollectionsTest.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.collections; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.List; 6 | import java.util.stream.IntStream; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | import static org.junit.jupiter.api.Assertions.assertThrows; 10 | 11 | public class ImmutableCollectionsTest { 12 | 13 | private Integer[] intArgs(int n) { 14 | return IntStream.rangeClosed(1, n) 15 | .boxed() 16 | .toArray(Integer[]::new); 17 | } 18 | 19 | @Test 20 | public void createImmutableList() { 21 | IntStream.rangeClosed(1, 10) 22 | .forEach(n -> { 23 | List intList = List.of(intArgs(n)); 24 | assertEquals(n, intList.size()); 25 | assertEquals(1, intList.get(0).intValue()); 26 | assertEquals(n, intList.get(intList.size() - 1).intValue()); 27 | }); 28 | } 29 | 30 | @Test 31 | public void showImmutabilityAdd() { 32 | List intList = List.of(1, 2, 3); 33 | assertThrows(UnsupportedOperationException.class, () -> intList.add(99)); 34 | } 35 | 36 | @Test 37 | public void showImmutabilityClear() { 38 | List intList = List.of(1, 2, 3); 39 | assertThrows(UnsupportedOperationException.class, intList::clear); 40 | } 41 | 42 | @Test 43 | public void showImmutabilityRemove() { 44 | List intList = List.of(1, 2, 3); 45 | assertThrows(UnsupportedOperationException.class, () -> intList.remove(0)); 46 | } 47 | 48 | @Test 49 | public void showImmutabilityReplace() { 50 | List intList = List.of(1, 2, 3); 51 | assertThrows(UnsupportedOperationException.class, () -> intList.replaceAll(n -> -n)); 52 | } 53 | 54 | @Test 55 | public void showImmutabilitySet() { 56 | List intList = List.of(1, 2, 3); 57 | assertThrows(UnsupportedOperationException.class, () -> intList.set(0, 99)); 58 | } 59 | 60 | @Test 61 | public void areWeImmutableOrArentWe() { 62 | List holders = List.of(new Holder(1), new Holder(2)); 63 | assertEquals(1, holders.get(0).getX()); 64 | 65 | holders.get(0).setX(4); 66 | assertEquals(4, holders.get(0).getX()); 67 | } 68 | 69 | @Test 70 | public void testVarargsList() { 71 | List intList = List.of(intArgs(11)); 72 | assertEquals(11, intList.size()); 73 | assertEquals( 1, intList.get(0).intValue()); 74 | assertEquals(11, intList.get(intList.size() - 1).intValue()); 75 | } 76 | } -------------------------------------------------------------------------------- /src/test/java/com/kousenit/recipes/collections/ImmutableMapTest.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.collections; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.Collection; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | import static java.util.Map.entry; 11 | import static java.util.Map.ofEntries; 12 | import static org.junit.jupiter.api.Assertions.*; 13 | 14 | @SuppressWarnings("ResultOfMethodCallIgnored") 15 | public class ImmutableMapTest { 16 | 17 | @Test 18 | void setOf() { 19 | Set letters = Set.of("a", "b"); 20 | assertEquals(2, letters.size()); 21 | } 22 | 23 | @Test 24 | void listOf() { 25 | List letters = List.of("a", "b"); 26 | assertEquals(2, letters.size()); 27 | } 28 | 29 | @Test 30 | void addingElementThrowsUOE() { 31 | List strings = List.of("a", "b"); 32 | assertThrows(UnsupportedOperationException.class, 33 | () -> strings.add("c")); 34 | } 35 | 36 | @Test 37 | void setOfDuplicates() { 38 | assertThrows(IllegalArgumentException.class, () -> Set.of("a", "a")); 39 | } 40 | 41 | @Test 42 | void mapWithNulls() { 43 | assertAll("No null keys or values", 44 | () -> assertThrows(NullPointerException.class, () -> Map.of(null, "value")), 45 | () -> assertThrows(NullPointerException.class, () -> Map.of("key", null))); 46 | } 47 | 48 | @Test 49 | void noDuplicateKeysInMap() { 50 | assertThrows(IllegalArgumentException.class, () -> 51 | ofEntries(entry("k1", "v1"), 52 | entry("k2", "v1"), 53 | entry("k1", "v2"))); 54 | } 55 | 56 | @Test 57 | public void immutableMapFromEntries() { 58 | Map jvmLanguages = ofEntries( 59 | entry("Java", "http://www.oracle.com/technetwork/java/index.html"), 60 | entry("Groovy", "http://groovy-lang.org/"), 61 | entry("Scala", "http://www.scala-lang.org/"), 62 | entry("Clojure", "https://clojure.org/"), 63 | entry("Kotlin", "http://kotlinlang.org/")); 64 | 65 | Set names = Set.of("Java", "Scala", "Groovy", "Clojure", "Kotlin"); 66 | 67 | List urls = List.of( 68 | "http://www.oracle.com/technetwork/java/index.html", 69 | "http://groovy-lang.org/", 70 | "http://www.scala-lang.org/", 71 | "https://clojure.org/", 72 | "http://kotlinlang.org/"); 73 | 74 | Set keys = jvmLanguages.keySet(); 75 | Collection values = jvmLanguages.values(); 76 | names.forEach(name -> assertTrue(keys.contains(name))); 77 | urls.forEach(url -> assertTrue(values.contains(url))); 78 | 79 | Map javaMap = Map.of( 80 | "Java", 81 | "http://www.oracle.com/technetwork/java/index.html", 82 | "Groovy", 83 | "http://groovy-lang.org/", 84 | "Scala", 85 | "http://www.scala-lang.org/", 86 | "Clojure", 87 | "https://clojure.org/", 88 | "Kotlin", 89 | "http://kotlinlang.org/"); 90 | javaMap.forEach((name, url) -> assertTrue( 91 | jvmLanguages.containsKey(name) && jvmLanguages.containsValue(url))); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/com/kousenit/recipes/datetime/DateRangeTest.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.datetime; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.time.LocalDate; 6 | import java.util.List; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | public class DateRangeTest { 11 | private DateRange range = new DateRange(); 12 | 13 | @Test 14 | public void dateRange_oneWeekInDays() { 15 | LocalDate now = LocalDate.now(); 16 | LocalDate then = now.plusWeeks(1); 17 | 18 | List dates = range.getDays_java9(now, then); 19 | 20 | assertEquals(7, dates.size()); 21 | assertEquals(dates, range.getDays_java8(now, then)); 22 | } 23 | 24 | @Test 25 | public void dateRange_oneYearInMonths() { 26 | LocalDate now = LocalDate.now(); 27 | LocalDate then = now.plusYears(1); 28 | 29 | List dates = range.getMonths_java9(now, then); 30 | 31 | assertEquals(12, dates.size()); 32 | assertEquals(dates, range.getMonths_java8(now, then)); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/test/java/com/kousenit/recipes/generics/SafeVaragsDemoTest.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.generics; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.time.LocalDate; 6 | import java.util.Arrays; 7 | 8 | import static org.hamcrest.MatcherAssert.assertThat; 9 | import static org.hamcrest.Matchers.arrayContaining; 10 | import static org.junit.jupiter.api.Assertions.assertThrows; 11 | 12 | class SafeVaragsDemoTest { 13 | private SafeVaragsDemo demo = new SafeVaragsDemo(); 14 | private LocalDate now = LocalDate.now(); 15 | 16 | @Test 17 | void replaceFirstStringWithDate() { 18 | String[] strings = "s1 s2 s3".split("\\s"); 19 | demo.replaceFirstStringWithDate(now, strings); 20 | 21 | assertThat(strings, arrayContaining(now.toString(), "s2", "s3")); 22 | } 23 | 24 | @Test 25 | void replaceFirstValueInArray() { 26 | String[] strings = "s1 s2 s3".split("\\s"); 27 | 28 | assertThrows(ArrayStoreException.class, 29 | () -> demo.replaceFirstValueInArray(now, strings)); 30 | } 31 | 32 | @Test 33 | void replaceFirstValueInArrayUsingVarargs() { 34 | assertThrows(ClassCastException.class, 35 | () -> { 36 | Comparable>[] comparables = 37 | demo.replaceFirstValueInArray(now, "s1", "s2", "s3"); 38 | System.out.println(Arrays.toString(comparables)); 39 | }); 40 | } 41 | 42 | @Test 43 | void tickingTimeBomb() { 44 | // No exception thrown until you access any element 45 | demo.replaceFirstValueInArray(now, "s1", "s2", "s2"); 46 | } 47 | } -------------------------------------------------------------------------------- /src/test/java/com/kousenit/recipes/http/AstroClientTest.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.http; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | class AstroClientTest { 6 | private AstroClient client = new AstroClient(); 7 | 8 | @Test 9 | void checkJsonOutput() { 10 | System.out.println(client.getJsonResponse()); 11 | } 12 | } -------------------------------------------------------------------------------- /src/test/java/com/kousenit/recipes/http/JokeClientTest.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.http; 2 | 3 | import org.junit.jupiter.api.Disabled; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.io.IOException; 7 | import java.util.concurrent.ExecutionException; 8 | import java.util.logging.Logger; 9 | 10 | import static org.junit.jupiter.api.Assertions.*; 11 | 12 | @Disabled("Service no longer available") 13 | class JokeClientTest { 14 | private final Logger logger = Logger.getLogger(JokeClientTest.class.getName()); 15 | 16 | private final JokeClient client = new JokeClient(); 17 | private final String heroFirstName = "Derek"; 18 | private final String heroLastName = "Hakim"; 19 | 20 | @Test 21 | void getJokeSync() throws IOException, InterruptedException { 22 | String joke = client.getJokeSync(heroFirstName, heroLastName); 23 | logger.info(joke); 24 | assertTrue(joke.contains(heroFirstName) || 25 | joke.contains(heroLastName)); 26 | } 27 | 28 | @Test 29 | void getJokeAsync() throws ExecutionException, InterruptedException { 30 | String joke = client.getJokeAsync(heroFirstName, heroLastName); 31 | logger.info(joke); 32 | assertTrue(joke.contains(heroFirstName) || 33 | joke.contains(heroLastName)); 34 | } 35 | } -------------------------------------------------------------------------------- /src/test/java/com/kousenit/recipes/interfaces/PrivateDemoTest.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.interfaces; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | 7 | public class PrivateDemoTest { 8 | private PrivateDemo demo = new PrivateDemo(); 9 | 10 | @Test 11 | public void addEvens() { 12 | int sum = demo.addEvens(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 13 | assertEquals(30, sum); 14 | } 15 | 16 | @Test 17 | public void addOdds() { 18 | int sum = demo.addOdds(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 19 | assertEquals(25, sum); 20 | } 21 | } -------------------------------------------------------------------------------- /src/test/java/com/kousenit/recipes/interfaces/SumNumbersTest.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.interfaces; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.lang.reflect.Method; 6 | import java.util.List; 7 | import java.util.function.IntPredicate; 8 | import java.util.stream.Collectors; 9 | import java.util.stream.IntStream; 10 | import java.util.stream.Stream; 11 | 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | import static org.hamcrest.Matchers.containsInAnyOrder; 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | 16 | public class SumNumbersTest { 17 | private SumNumbers demo = new PrivateDemo(); 18 | 19 | @Test 20 | public void addEvens() { 21 | assertEquals(2 + 4 + 6, demo.addEvens(1, 2, 3, 4, 5, 6)); 22 | } 23 | 24 | @Test 25 | public void addOdds() { 26 | assertEquals(1 + 3 + 5, demo.addOdds(1, 2, 3, 4, 5, 6)); 27 | } 28 | 29 | private List nonObjectMethods(Method[] methods) { 30 | return Stream.of(methods) 31 | .filter(method -> !method.getDeclaringClass().equals(Object.class)) 32 | .collect(Collectors.toList()); 33 | } 34 | 35 | @Test 36 | public void checkMethods() throws Exception { 37 | List methods = nonObjectMethods(demo.getClass().getDeclaredMethods()); 38 | assertEquals(0, methods.size()); 39 | 40 | methods = nonObjectMethods(demo.getClass().getMethods()); 41 | assertEquals(2, methods.size()); 42 | 43 | List names = methods.stream() 44 | .map(Method::getName) 45 | .collect(Collectors.toList()); 46 | String[] correct = new String[]{"addEvens", "addOdds"}; 47 | assertThat(names, containsInAnyOrder(correct)); 48 | 49 | Class[] interfaces = demo.getClass().getInterfaces(); 50 | assertEquals(1, interfaces.length); 51 | assertEquals(SumNumbers.class, interfaces[0]); 52 | 53 | Method add = interfaces[0] 54 | .getDeclaredMethod("add", IntPredicate.class, int[].class); 55 | add.setAccessible(true); 56 | int[] ints = IntStream.rangeClosed(1, 5).toArray(); 57 | IntPredicate predicate = e -> true; 58 | Object result = add.invoke(demo, predicate, ints); 59 | assertEquals(1 + 2 + 3 + 4 + 5, result); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /src/test/java/com/kousenit/recipes/lvti/VarTypeTest.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.lvti; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertTrue; 12 | 13 | @SuppressWarnings({"UnnecessaryBoxing", "ConstantConditions", "UnnecessaryLocalVariable", "MismatchedQueryAndUpdateOfCollection"}) 14 | public class VarTypeTest { 15 | @Test 16 | void inferString() { 17 | var s = "Hello, World!"; 18 | assertEquals(String.class, s.getClass()); 19 | } 20 | 21 | @Test 22 | void inferInteger() { 23 | var num = 3; 24 | assertTrue(Integer.valueOf(num) instanceof Integer); 25 | } 26 | 27 | @Test 28 | void inferListOfString() { 29 | var strings = List.of("this", "is", "a", "list", "of", "strings"); 30 | System.out.println(strings.getClass().getName()); 31 | assertTrue(strings instanceof List); 32 | 33 | List nums = new ArrayList<>(); 34 | nums.add(3); nums.add(1); nums.add(4); 35 | var numsVar = nums; 36 | System.out.println(numsVar.getClass().getName()); 37 | assertEquals(ArrayList.class, numsVar.getClass()); 38 | } 39 | 40 | @Test 41 | void inferArrayListOfString() { 42 | var strings = new ArrayList(); 43 | assertEquals(ArrayList.class, strings.getClass()); 44 | } 45 | 46 | @Test 47 | void loopOverComplicatedMap() { 48 | Map> map = Map.ofEntries( 49 | Map.entry("a", List.of(1, 2, 3)), 50 | Map.entry("b", List.of(1, 2, 3)), 51 | Map.entry("c", List.of(1, 2, 3)), 52 | Map.entry("d", List.of(1, 2, 3))); 53 | 54 | // LVTI useful in for loops and try-with-resource blocks 55 | for (var e : map.entrySet()) { 56 | System.out.println(e.getKey() + ": " + e.getValue()); 57 | } 58 | } 59 | 60 | @Test 61 | void inferredAnonymousType() { 62 | var v = new Runnable() { 63 | public void run() { 64 | System.out.println("Running..."); 65 | } 66 | 67 | public void runTwice() { 68 | run(); 69 | run(); 70 | } 71 | }; 72 | 73 | v.runTwice(); 74 | } 75 | 76 | @Test 77 | void nullProblem() { 78 | // var x = null; 79 | } 80 | 81 | @Test 82 | void dontDoThis() { 83 | var var = new Var("var"); 84 | } 85 | } 86 | 87 | class Var { 88 | private String var; 89 | 90 | public Var(String var) { 91 | this.var = var; 92 | } 93 | 94 | public String getVar() { 95 | return var; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/com/kousenit/recipes/streams/StreamTests.java: -------------------------------------------------------------------------------- 1 | package com.kousenit.recipes.streams; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.math.BigDecimal; 6 | import java.util.Arrays; 7 | import java.util.Comparator; 8 | import java.util.List; 9 | import java.util.Random; 10 | import java.util.stream.Collectors; 11 | import java.util.stream.Stream; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | import static org.junit.jupiter.api.Assertions.assertTrue; 15 | 16 | public class StreamTests { 17 | @Test 18 | public void ofNullable() { 19 | Stream stream = Stream.ofNullable("abc"); 20 | assertEquals(1, stream.count()); 21 | 22 | stream = Stream.ofNullable(null); 23 | assertEquals(0, stream.count()); 24 | } 25 | 26 | @Test 27 | public void iterate() { 28 | List bigDecimals = 29 | Stream.iterate(BigDecimal.ZERO, bd -> bd.add(BigDecimal.ONE)) 30 | .limit(10) 31 | .collect(Collectors.toList()); 32 | 33 | assertEquals(10, bigDecimals.size()); 34 | 35 | bigDecimals = Stream.iterate(BigDecimal.ZERO, 36 | bd -> bd.longValue() < 10L, 37 | bd -> bd.add(BigDecimal.ONE)) 38 | .collect(Collectors.toList()); 39 | 40 | assertEquals(10, bigDecimals.size()); 41 | } 42 | 43 | @Test 44 | public void takeWhile() { 45 | List strings = Stream.of("this is a list of strings".split(" ")) 46 | .takeWhile(s -> !s.equals("of")) 47 | .collect(Collectors.toList()); 48 | List correct = Arrays.asList("this", "is", "a", "list"); 49 | assertEquals(correct, strings); 50 | } 51 | 52 | @Test 53 | public void dropWhile() { 54 | List strings = Stream.of("this is a list of strings".split(" ")) 55 | .dropWhile(s -> !s.equals("of")) 56 | .collect(Collectors.toList()); 57 | List correct = Arrays.asList("of", "strings"); 58 | assertEquals(correct, strings); 59 | } 60 | 61 | @Test 62 | public void takeWhileRandom() { 63 | Random random = new Random(); 64 | List nums = random.ints(50, 0, 100) 65 | .boxed() 66 | .sorted(Comparator.reverseOrder()) 67 | .takeWhile(n -> n > 90) 68 | .collect(Collectors.toList()); 69 | 70 | System.out.println(nums); 71 | nums.forEach(n -> assertTrue(n > 70)); 72 | } 73 | 74 | @Test 75 | public void dropWhileRandom() { 76 | Random random = new Random(); 77 | List nums = random.ints(50, 0, 100) 78 | .sorted() 79 | .dropWhile(n -> n < 90) 80 | .boxed() 81 | .collect(Collectors.toList()); 82 | 83 | System.out.println(nums); 84 | nums.forEach(n -> assertTrue(n > 70)); 85 | } 86 | 87 | } 88 | --------------------------------------------------------------------------------