├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.gradle ├── descriptor.json ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── intellij1.png ├── intellij2.png ├── lib └── annotations-java8.jar ├── null4jlogosmall.png ├── settings.gradle └── src ├── main └── java │ └── biz │ └── cosee │ └── null4j │ ├── NotNullByDefault.java │ └── Null4j.java └── test └── java └── biz └── cosee └── null4j └── Null4jTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 2 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 3 | 4 | # User-specific stuff: 5 | .idea/**/workspace.xml 6 | .idea/**/tasks.xml 7 | .idea/dictionaries 8 | 9 | # Sensitive or high-churn files: 10 | .idea/**/dataSources/ 11 | .idea/**/dataSources.ids 12 | .idea/**/dataSources.xml 13 | .idea/**/dataSources.local.xml 14 | .idea/**/sqlDataSources.xml 15 | .idea/**/dynamic.xml 16 | .idea/**/uiDesigner.xml 17 | 18 | # Gradle: 19 | .idea/**/gradle.xml 20 | .idea/**/libraries 21 | 22 | # CMake 23 | cmake-build-debug/ 24 | 25 | # Mongo Explorer plugin: 26 | .idea/**/mongoSettings.xml 27 | 28 | ## File-based project format: 29 | *.iws 30 | 31 | ## Plugin-specific files: 32 | 33 | # IntelliJ 34 | /out/ 35 | 36 | # mpeltonen/sbt-idea plugin 37 | .idea_modules/ 38 | 39 | # JIRA plugin 40 | atlassian-ide-plugin.xml 41 | 42 | # Cursive Clojure plugin 43 | .idea/replstate.xml 44 | 45 | # Crashlytics plugin (for Android Studio and IntelliJ) 46 | com_crashlytics_export_strings.xml 47 | crashlytics.properties 48 | crashlytics-build.properties 49 | fabric.properties 50 | 51 | # Own ignore stuff 52 | # Why would I need the idea stuff? 53 | .idea/* 54 | 55 | *.swp 56 | *.iml 57 | target 58 | 59 | # vim 60 | .swo 61 | 62 | 63 | # gradle 64 | .gradle 65 | /build/ 66 | 67 | # Ignore Gradle GUI config 68 | gradle-app.setting 69 | 70 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 71 | !gradle-wrapper.jar 72 | 73 | # Cache of project 74 | .gradletasknamecache 75 | 76 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 77 | # gradle/wrapper/gradle-wrapper.properties 78 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | before_install: 5 | - chmod +x gradlew 6 | 7 | before_deploy: 8 | - ./gradlew generatePomFileForMavenPublication 9 | - ./gradlew sourcesJar 10 | 11 | deploy: 12 | provider: bintray 13 | file: "descriptor.json" 14 | user: $BINTRAY_API_USER 15 | key: $BINTRAY_API_KEY 16 | dry-run: false 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 cosee GmbH, https://cosee.biz 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![null4j logo](null4jlogosmall.png) 2 | 3 | With null4j, your code will have fewer NullPointerExceptions and more readable null checks. 4 | 5 | _v1.0.0_ 6 | 7 | ```java 8 | @NotNullByDefault 9 | class Example { 10 | 11 | // only annotate nullable fields, everything else will be NotNull by default 12 | @Getter class Person { Integer id; @Nullable Address; } 13 | @Getter class Address { @Nullable Street street; } 14 | @Getter class Street { String streetName; } 15 | 16 | @Nullable Street getStreet(Person person) { 17 | // Use safe navigation to reach into objects without NPE 18 | return let(person, 19 | Person::getAddress, 20 | Address::getStreet); 21 | } 22 | 23 | // implicitly NotNull 24 | String getStreetName(Person person) { 25 | 26 | // avoid returning null by using a default value 27 | return requireNonNullElse(let(person, 28 | Person::getAddress, 29 | Address::getStreet, 30 | Street::getStreetName 31 | ), "could not find street name"); 32 | 33 | } 34 | } 35 | ``` 36 | 37 | ## Features 38 | 39 | To keep it simple, null4j only contains three features: 40 | 41 | - @NotNullByDefault 42 | - requireNonNullElse 43 | - let 44 | 45 | ### @NotNullByDefault 46 | 47 | This meta annotation can be applied to classes and packages. Everything will be implicitely annotated with @NotNull unless it is explicitely annotated with @Nullable. 48 | 49 | #### Annotating classes 50 | 51 | Using the annotation on a class works like usual: 52 | 53 | ```java 54 | import biz.cosee.null4j.NotNullByDefault; 55 | 56 | @NotNullByDefault 57 | class SomeClass { /* ... */ } 58 | ``` 59 | 60 | #### Annotating packages 61 | 62 | Annotating packages requires you to create a package-info.java file in the package that you want to annotate. 63 | 64 | A package-info.java file would look like this: 65 | 66 | ```java 67 | @NotNullByDefault 68 | package com.example.the.package.that.contains.this.package.info.java.file; 69 | 70 | import biz.cosee.null4j.NotNullByDefault; 71 | ``` 72 | 73 | The order is important: It won't work if you put the import before the annotation. 74 | 75 | Note: The annotation does not work recursively yet so sub packages need their own package-info.java. 76 | 77 | ### requireNonNullElse 78 | 79 | ```java 80 | T requireNonNullElse((@Nullable T)... nullables, T defaultValue) 81 | ``` 82 | 83 | Returns the first not null parameter. The last parameter must not be null. 84 | 85 | This is similar to SQL's coalesce or Javascript's || except that having null as the last parameter is not allowed. 86 | 87 | #### Example 88 | 89 | ```java 90 | // Java 7: 91 | 92 | String s = someMap.get(key); 93 | if(s == null) { 94 | s = ""; 95 | } 96 | 97 | doSomethingWith(s); 98 | 99 | 100 | // Java 8: 101 | 102 | String s = someMap.getrequireNonNullElse(key, ""); 103 | 104 | doSomethingWith(s); 105 | 106 | 107 | // null4j: 108 | 109 | String s = requireNonNullElse(someMap.get(key), ""); 110 | 111 | doSomethingWith(s); 112 | 113 | 114 | void example(@Nullable Thing thing) { 115 | 116 | Thing t = requireNonNullElse(thing, Things.STANDARD_THING); 117 | 118 | doSomethingWith(t); 119 | 120 | } 121 | ``` 122 | 123 | ### let 124 | 125 | A fluent map/flatMap for nullable types that works similar to Optional::map and Optional::flatMap. 126 | 127 | ```java 128 | <*> @Nullable * let(@Nullable * value, Function<*, @Nullable *>... functions) 129 | ``` 130 | 131 | The parameters must form a typed aligned sequence. If the last parameter is a Consumer, let returns void. 132 | 133 | #### Example 134 | 135 | ```java 136 | @Nullable String getName() { /* ... */ } 137 | 138 | Map someMap = // ... 139 | 140 | 141 | void example() { 142 | 143 | // like map 144 | @Nullable String nullableUpperCaseName = let(getName(), String::toUpperCase); 145 | 146 | 147 | // map/flatMap chain that returns void and may or may not print to System.out 148 | let(nullableUpperCaseName, 149 | someMap::get, 150 | System.out::println 151 | ); 152 | 153 | } 154 | ``` 155 | 156 | ## Mockito support 157 | 158 | It will flag errors when you try to mock something with wrong types e.g. if the original method never returns null mocking it as always returning null will be an error. 159 | 160 | ## Lombok support 161 | 162 | Nullability annotations are copied to generated getters/setters. 163 | 164 | ```java 165 | @Data 166 | class Thing { 167 | String name; 168 | @Nullable String description; 169 | } 170 | 171 | void example() { 172 | Thing thing = getSomeThing(); 173 | 174 | // fine 175 | thing.getName().toUpperCase(); 176 | 177 | // Will be flagged by IntelliJ 178 | thing.getDescription().toUpperCase();❌ 179 | 180 | // fine 181 | let(thing.getDescription(), String::toUpperCase); 182 | } 183 | ``` 184 | 185 | ## Examples 186 | 187 | ### Default Parameters 188 | 189 | If method overloading is not an option because the method has too many parameters, requireNonNullElse can be used to declare default values for nullable parameters. 190 | 191 | ```java 192 | void displayInfo( 193 | String id, 194 | @Nullable String name, 195 | @Nullable String comment, 196 | Address address, 197 | @Nullable String designation 198 | ) { 199 | // Set defaults for nullable parameters at the beginning of the method. 200 | name = requireNonNullElse(name, "no name"); 201 | comment = requireNonNullElse(comment, ""); 202 | designation = requireNonNullElse(designation, "no designation"); 203 | 204 | 205 | // ... 206 | } 207 | ``` 208 | 209 | ### Safe Navigation 210 | 211 | Suppose you have some nested types. With let, you can easily reach into them without worrying about nullpointer exceptions. 212 | 213 | ```java 214 | @Data class Person { int id; @Nullable Address; } 215 | @Data class Address { @Nullable Street street; } 216 | @Data class Street { String name; } 217 | 218 | @Nullable String getStreetName(Person person) { 219 | return let(person, 220 | Person::getAddress, 221 | Address::getStreet, 222 | Street::getName); 223 | } 224 | ``` 225 | 226 | ### Combining requireNonNullElse and let 227 | 228 | ```java 229 | return requireNonNullElse(let(person, 230 | Person::getId, 231 | commentMap::get, 232 | String::toUpperCase 233 | ), "NO COMMENT"); 234 | ``` 235 | 236 | ### Nesting let to bind multiple values at once 237 | 238 | This is similar to declaring variables, except that the null checks are included already. 239 | 240 | ```java 241 | let(getNullableA(), a -> 242 | let(getNullableB(), b -> 243 | let(getNullableC(), c -> { 244 | 245 | System.out.println("All three are not null"); 246 | 247 | doSomethingWithAllThree(a, b, c); 248 | 249 | }))); 250 | ``` 251 | 252 | ## Refactoring existing code 253 | 254 | ### Annotate classes instead of packages 255 | 256 | Experience shows that using @NotNullByDefault as a package annotation in legacy projects can be confusing: When you see a class that has no @NotNullByDefault annotation, is that because the package is already annotated or was it never annotated? The package annotation can also lead to problems when moving a class between annotated and non-annonated packages. 257 | 258 | Only use the package annotation in new projects or when you annotated all of your legacy code. 259 | 260 | ### Remove null handling from functions. 261 | 262 | Avoid code like this: 263 | 264 | ```java 265 | // bad code 266 | 267 | @Nullable String format(@Nullable Thing thing) { 268 | // this function does two things at once 269 | if(thing == null) { 270 | return null; 271 | } else { 272 | // actually relevant code 273 | return "It's very " + thing.getQuality(); 274 | } 275 | } 276 | 277 | void example(@Nullable Thing nullableThing, Thing thing) { 278 | @Nullable String a = format(nullableThing); 279 | 280 | // this is actually never null. 281 | @Nullable String b = format(thing); 282 | 283 | // ... 284 | } 285 | ``` 286 | 287 | Instead, simplify your functions by moving the null handling to the call site: 288 | 289 | 290 | ```java 291 | // simple: function does only one thing 292 | String format(Thing thing) { 293 | return "It's very " + thing.getQuality(); 294 | } 295 | 296 | void example(@Nullable Thing nullableThing, Thing thing) { 297 | 298 | @Nullable String a = let(nullableThing, this::format); 299 | 300 | // no null handling needed 301 | String b = format(thing); 302 | 303 | // ... 304 | } 305 | ``` 306 | 307 | Now the format function only does one thing instead of two and the type checker can verify that b is never null. 308 | 309 | ## Setup 310 | 311 | Using null4j is a two step process: Add the library to your pom/gradle file, then configure IntelliJ to perform stricter tests. 312 | 313 | ### Dependency 314 | 315 | It's currently available on JCenter. Adding it to Maven Central is planned. 316 | 317 | #### Maven 318 | 319 | ```xml 320 | 321 | 322 | biz.cosee.null4j 323 | null4j 324 | 1.0.0 325 | 326 | 327 | 328 | 329 | jcenter 330 | http://jcenter.bintray.com 331 | 332 | 333 | ``` 334 | 335 | #### Gradle 336 | 337 | ```groovy 338 | repositories { 339 | jcenter() 340 | } 341 | 342 | dependencies { 343 | compile 'biz.cosee.null4j:null4j:1.0.0' 344 | } 345 | ``` 346 | 347 | ### IntelliJ 348 | 349 | You need to change two checker settings: 350 | 351 | ![Constant conditions & exceptions](intellij1.png) 352 | 353 | ![@NotNull/@Nullable problems](intellij2.png) 354 | 355 | ## Contributing 356 | 357 | Contributions are welcome! Contact Michael Zinn on [Github](https://github.com/RedNifre) or [Twitter](https://twitter.com/RedNifre). 358 | 359 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | group 'biz.cosee.null4j' 2 | version '1.0.0' 3 | 4 | apply plugin: 'java' 5 | 6 | sourceCompatibility = 1.8 7 | 8 | repositories { 9 | jcenter() 10 | } 11 | 12 | dependencies { 13 | testCompile group: 'junit', name: 'junit', version: '4.11' 14 | testCompile 'org.assertj:assertj-core:3.8.0' 15 | 16 | compile 'com.google.code.findbugs:jsr305:1.3.9' 17 | compile files('lib/annotations-java8.jar') 18 | } 19 | 20 | task sourcesJar(type: Jar, dependsOn: classes) { 21 | classifier = 'sources' 22 | from sourceSets.main.allSource 23 | } 24 | 25 | apply plugin: 'maven-publish' 26 | 27 | publishing { 28 | publications { 29 | maven(MavenPublication) { 30 | from components.java 31 | pom.withXml { 32 | asNode().dependencies.'*'.findAll() { 33 | it.scope.text() == 'runtime' && project.configurations.compile.allDependencies.find { dep -> 34 | dep.name == it.artifactId.text() 35 | } 36 | }.each() { 37 | it.scope*.value = 'compile' 38 | } 39 | } 40 | } 41 | } 42 | } 43 | 44 | model { 45 | tasks.generatePomFileForMavenPublication { 46 | destination = file("$buildDir/libs/null4j-1.0.0.pom") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /descriptor.json: -------------------------------------------------------------------------------- 1 | { 2 | "package": { 3 | "name": "null4j", 4 | "repo": "null4j", 5 | "subject": "cosee", 6 | "desc": "Simplified null handling", 7 | "website_url": "https://github.com/cosee/null4j", 8 | "issue_tracker_url": "https://github.com/cosee/null4j/issues", 9 | "vcs_url": "https://github.com/cosee/null4j", 10 | "licenses": ["Apache-2.0"], 11 | "labels": ["java"], 12 | "public_download_numbers": false, 13 | "public_stats": false 14 | }, 15 | 16 | "version": { 17 | "name": "1.0.0", 18 | "released": "2017-12-07", 19 | "gpgSign": false 20 | }, 21 | 22 | "files": [ 23 | {"includePattern": "build/libs/(.*)", "uploadPattern": "biz/cosee/null4j/null4j/1.0.0/$1"} 24 | ], 25 | "publish": true 26 | } 27 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosee/null4j/991a1bbaf220fb1b6ddd6f0a8f2ec0b5e480f05c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon May 29 11:28:19 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /intellij1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosee/null4j/991a1bbaf220fb1b6ddd6f0a8f2ec0b5e480f05c/intellij1.png -------------------------------------------------------------------------------- /intellij2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosee/null4j/991a1bbaf220fb1b6ddd6f0a8f2ec0b5e480f05c/intellij2.png -------------------------------------------------------------------------------- /lib/annotations-java8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosee/null4j/991a1bbaf220fb1b6ddd6f0a8f2ec0b5e480f05c/lib/annotations-java8.jar -------------------------------------------------------------------------------- /null4jlogosmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosee/null4j/991a1bbaf220fb1b6ddd6f0a8f2ec0b5e480f05c/null4jlogosmall.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'null4j' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/biz/cosee/null4j/NotNullByDefault.java: -------------------------------------------------------------------------------- 1 | package biz.cosee.null4j; 2 | 3 | 4 | import javax.annotation.Nonnull; 5 | import javax.annotation.meta.TypeQualifierDefault; 6 | import java.lang.annotation.Documented; 7 | import java.lang.annotation.ElementType; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | /** 13 | * Makes nearly everything NotNull by default. 14 | * Can be added to classes and packages. 15 | * Does not apply recursively, i.e. you can't just annotate your root package. 16 | *

17 | * Created by Michael Zinn (@RedNifre), cosee GmbH on 04/04/17. 18 | */ 19 | @Documented 20 | @Nonnull 21 | @TypeQualifierDefault({ElementType.TYPE, ElementType.PARAMETER, ElementType.METHOD, ElementType.LOCAL_VARIABLE, ElementType.FIELD, ElementType.PACKAGE}) 22 | @Retention(RetentionPolicy.RUNTIME) 23 | @Target({ElementType.PACKAGE, ElementType.TYPE}) 24 | public @interface NotNullByDefault { 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/biz/cosee/null4j/Null4j.java: -------------------------------------------------------------------------------- 1 | package biz.cosee.null4j; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.function.Consumer; 7 | import java.util.function.Function; 8 | import java.util.function.Supplier; 9 | 10 | 11 | /** 12 | * Convenience functions for dealing with null. 13 | *

14 | * Created by Michael Zinn (@RedNifre), cosee GmbH on 30/03/17. 15 | */ 16 | public class Null4j { 17 | /** 18 | * Returns the first parameter that is not null. 19 | *

20 | * The last parameter must not be null. 21 | * 22 | * @param objs 23 | * @param defaultObj Must not be null. 24 | * @return First parameter that is not null 25 | */ 26 | public static 27 | @NotNull T requireNonNullElse( 28 | @Nullable T obj1, 29 | @NotNull T defaultObj 30 | ) { 31 | return 32 | obj1 != null ? obj1 : 33 | defaultObj; 34 | } 35 | 36 | /** 37 | * Returns the first parameter that is not null. 38 | *

39 | * The last parameter must not be null. 40 | * 41 | * @param objs 42 | * @param defaultObj Must not be null. 43 | * @return First parameter that is not null 44 | */ 45 | public static 46 | @NotNull T requireNonNullElse( 47 | @Nullable T obj1, 48 | @Nullable T obj2, 49 | @NotNull T defaultObj 50 | ) { 51 | return 52 | obj1 != null ? obj1 : 53 | obj2 != null ? obj2 : 54 | defaultObj; 55 | } 56 | 57 | /** 58 | * Returns the first parameter that is not null. 59 | *

60 | * The last parameter must not be null. 61 | * 62 | * @param objs 63 | * @param defaultObj Must not be null. 64 | * @return First parameter that is not null 65 | */ 66 | public static 67 | @NotNull T requireNonNullElse( 68 | @Nullable T obj1, 69 | @Nullable T obj2, 70 | @Nullable T obj3, 71 | @NotNull T defaultObj 72 | ) { 73 | return 74 | obj1 != null ? obj1 : 75 | obj2 != null ? obj2 : 76 | obj3 != null ? obj3 : 77 | defaultObj; 78 | } 79 | 80 | /** 81 | * Returns the first parameter that is not null. 82 | *

83 | * The last parameter must not be null. 84 | * 85 | * @param objs 86 | * @param defaultObj Must not be null. 87 | * @return First parameter that is not null 88 | */ 89 | public static 90 | @NotNull T requireNonNullElse( 91 | @Nullable T obj1, 92 | @Nullable T obj2, 93 | @Nullable T obj3, 94 | @Nullable T obj4, 95 | @NotNull T defaultObj 96 | ) { 97 | return 98 | obj1 != null ? obj1 : 99 | obj2 != null ? obj2 : 100 | obj3 != null ? obj3 : 101 | obj4 != null ? obj4 : 102 | defaultObj; 103 | } 104 | 105 | /** 106 | * Returns the first parameter that is not null. 107 | *

108 | * The last parameter must not be null. 109 | * 110 | * @param objs 111 | * @param defaultObj Must not be null. 112 | * @return First parameter that is not null 113 | */ 114 | public static 115 | @NotNull T requireNonNullElse( 116 | @Nullable T obj1, 117 | @Nullable T obj2, 118 | @Nullable T obj3, 119 | @Nullable T obj4, 120 | @Nullable T obj5, 121 | @NotNull T defaultObj 122 | ) { 123 | return 124 | obj1 != null ? obj1 : 125 | obj2 != null ? obj2 : 126 | obj3 != null ? obj3 : 127 | obj4 != null ? obj4 : 128 | obj5 != null ? obj5 : 129 | defaultObj; 130 | } 131 | 132 | /** 133 | * Returns the first parameter that is not null. 134 | *

135 | * The last parameter must not be null. 136 | * 137 | * @param objs 138 | * @param defaultObj Must not be null. 139 | * @return First parameter that is not null 140 | */ 141 | public static 142 | @NotNull T requireNonNullElse( 143 | @Nullable T obj1, 144 | @Nullable T obj2, 145 | @Nullable T obj3, 146 | @Nullable T obj4, 147 | @Nullable T obj5, 148 | @Nullable T obj6, 149 | @NotNull T defaultObj 150 | ) { 151 | return 152 | obj1 != null ? obj1 : 153 | obj2 != null ? obj2 : 154 | obj3 != null ? obj3 : 155 | obj4 != null ? obj4 : 156 | obj5 != null ? obj5 : 157 | obj6 != null ? obj6 : 158 | defaultObj; 159 | } 160 | 161 | /** 162 | * Returns the first parameter that is not null. 163 | *

164 | * The last parameter must not be null. 165 | * 166 | * @param objs 167 | * @param defaultObj Must not be null. 168 | * @return First parameter that is not null 169 | */ 170 | public static 171 | @NotNull T requireNonNullElse( 172 | @Nullable T obj1, 173 | @Nullable T obj2, 174 | @Nullable T obj3, 175 | @Nullable T obj4, 176 | @Nullable T obj5, 177 | @Nullable T obj6, 178 | @Nullable T obj7, 179 | @NotNull T defaultObj 180 | ) { 181 | return 182 | obj1 != null ? obj1 : 183 | obj2 != null ? obj2 : 184 | obj3 != null ? obj3 : 185 | obj4 != null ? obj4 : 186 | obj5 != null ? obj5 : 187 | obj6 != null ? obj6 : 188 | obj7 != null ? obj7 : 189 | defaultObj; 190 | } 191 | 192 | /** 193 | * Returns the first parameter that is not null. 194 | *

195 | * The last parameter must not be null. 196 | * 197 | * @param objs 198 | * @param defaultObj Must not be null. 199 | * @return First parameter that is not null 200 | */ 201 | public static 202 | @NotNull T requireNonNullElse( 203 | @Nullable T obj1, 204 | @Nullable T obj2, 205 | @Nullable T obj3, 206 | @Nullable T obj4, 207 | @Nullable T obj5, 208 | @Nullable T obj6, 209 | @Nullable T obj7, 210 | @Nullable T obj8, 211 | @NotNull T defaultObj 212 | ) { 213 | return 214 | obj1 != null ? obj1 : 215 | obj2 != null ? obj2 : 216 | obj3 != null ? obj3 : 217 | obj4 != null ? obj4 : 218 | obj5 != null ? obj5 : 219 | obj6 != null ? obj6 : 220 | obj7 != null ? obj7 : 221 | obj8 != null ? obj8 : 222 | defaultObj; 223 | } 224 | 225 | /** 226 | * Returns the first parameter that is not null. 227 | *

228 | * The last parameter must not be null. 229 | * 230 | * @param objs 231 | * @param defaultObj Must not be null. 232 | * @return First parameter that is not null 233 | */ 234 | public static 235 | @NotNull T requireNonNullElse( 236 | @Nullable T obj1, 237 | @Nullable T obj2, 238 | @Nullable T obj3, 239 | @Nullable T obj4, 240 | @Nullable T obj5, 241 | @Nullable T obj6, 242 | @Nullable T obj7, 243 | @Nullable T obj8, 244 | @Nullable T obj9, 245 | @NotNull T defaultObj 246 | ) { 247 | return 248 | obj1 != null ? obj1 : 249 | obj2 != null ? obj2 : 250 | obj3 != null ? obj3 : 251 | obj4 != null ? obj4 : 252 | obj5 != null ? obj5 : 253 | obj6 != null ? obj6 : 254 | obj7 != null ? obj7 : 255 | obj8 != null ? obj8 : 256 | obj9 != null ? obj9 : 257 | defaultObj; 258 | } 259 | 260 | 261 | /** 262 | * Threads the first parameter through the following function parameters. 263 | *

264 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 265 | * 266 | * @param t Input value, may be null. 267 | * @param fs A typed sequence of functions. 268 | * @return Result or null if the last parameter is a function, otherwise void 269 | */ 270 | public static 271 | @Nullable U let( 272 | @Nullable T t, 273 | Function f_T_U 274 | ) { 275 | if (t == null) return null; 276 | @NotNull T nn_t = surely(t); 277 | return f_T_U.apply(nn_t); 278 | } 279 | 280 | /** 281 | * Threads the first parameter through the following function parameters. 282 | *

283 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 284 | * 285 | * @param t Input value, may be null. 286 | * @param fs A typed sequence of functions. 287 | * @return Result or null if the last parameter is a function, otherwise void 288 | */ 289 | public static 290 | @Nullable V let( 291 | @Nullable T t, 292 | Function f_T_U, 293 | Function f_U_V 294 | ) { 295 | if (t == null) return null; 296 | @NotNull T nn_t = surely(t); 297 | @Nullable U u = f_T_U.apply(nn_t); 298 | if (u == null) return null; 299 | @NotNull U nn_u = surely(u); 300 | return f_U_V.apply(nn_u); 301 | } 302 | 303 | /** 304 | * Threads the first parameter through the following function parameters. 305 | *

306 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 307 | * 308 | * @param t Input value, may be null. 309 | * @param fs A typed sequence of functions. 310 | * @return Result or null if the last parameter is a function, otherwise void 311 | */ 312 | public static 313 | @Nullable W let( 314 | @Nullable T t, 315 | Function f_T_U, 316 | Function f_U_V, 317 | Function f_V_W 318 | ) { 319 | if (t == null) return null; 320 | @NotNull T nn_t = surely(t); 321 | @Nullable U u = f_T_U.apply(nn_t); 322 | if (u == null) return null; 323 | @NotNull U nn_u = surely(u); 324 | @Nullable V v = f_U_V.apply(nn_u); 325 | if (v == null) return null; 326 | @NotNull V nn_v = surely(v); 327 | return f_V_W.apply(nn_v); 328 | } 329 | 330 | /** 331 | * Threads the first parameter through the following function parameters. 332 | *

333 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 334 | * 335 | * @param t Input value, may be null. 336 | * @param fs A typed sequence of functions. 337 | * @return Result or null if the last parameter is a function, otherwise void 338 | */ 339 | public static 340 | @Nullable X let( 341 | @Nullable T t, 342 | Function f_T_U, 343 | Function f_U_V, 344 | Function f_V_W, 345 | Function f_W_X 346 | ) { 347 | if (t == null) return null; 348 | @NotNull T nn_t = surely(t); 349 | @Nullable U u = f_T_U.apply(nn_t); 350 | if (u == null) return null; 351 | @NotNull U nn_u = surely(u); 352 | @Nullable V v = f_U_V.apply(nn_u); 353 | if (v == null) return null; 354 | @NotNull V nn_v = surely(v); 355 | @Nullable W w = f_V_W.apply(nn_v); 356 | if (w == null) return null; 357 | @NotNull W nn_w = surely(w); 358 | return f_W_X.apply(nn_w); 359 | } 360 | 361 | /** 362 | * Threads the first parameter through the following function parameters. 363 | *

364 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 365 | * 366 | * @param t Input value, may be null. 367 | * @param fs A typed sequence of functions. 368 | * @return Result or null if the last parameter is a function, otherwise void 369 | */ 370 | public static 371 | @Nullable Y let( 372 | @Nullable T t, 373 | Function f_T_U, 374 | Function f_U_V, 375 | Function f_V_W, 376 | Function f_W_X, 377 | Function f_X_Y 378 | ) { 379 | if (t == null) return null; 380 | @NotNull T nn_t = surely(t); 381 | @Nullable U u = f_T_U.apply(nn_t); 382 | if (u == null) return null; 383 | @NotNull U nn_u = surely(u); 384 | @Nullable V v = f_U_V.apply(nn_u); 385 | if (v == null) return null; 386 | @NotNull V nn_v = surely(v); 387 | @Nullable W w = f_V_W.apply(nn_v); 388 | if (w == null) return null; 389 | @NotNull W nn_w = surely(w); 390 | @Nullable X x = f_W_X.apply(nn_w); 391 | if (x == null) return null; 392 | @NotNull X nn_x = surely(x); 393 | return f_X_Y.apply(nn_x); 394 | } 395 | 396 | /** 397 | * Threads the first parameter through the following function parameters. 398 | *

399 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 400 | * 401 | * @param t Input value, may be null. 402 | * @param fs A typed sequence of functions. 403 | * @return Result or null if the last parameter is a function, otherwise void 404 | */ 405 | public static 406 | @Nullable Z let( 407 | @Nullable T t, 408 | Function f_T_U, 409 | Function f_U_V, 410 | Function f_V_W, 411 | Function f_W_X, 412 | Function f_X_Y, 413 | Function f_Y_Z 414 | ) { 415 | if (t == null) return null; 416 | @NotNull T nn_t = surely(t); 417 | @Nullable U u = f_T_U.apply(nn_t); 418 | if (u == null) return null; 419 | @NotNull U nn_u = surely(u); 420 | @Nullable V v = f_U_V.apply(nn_u); 421 | if (v == null) return null; 422 | @NotNull V nn_v = surely(v); 423 | @Nullable W w = f_V_W.apply(nn_v); 424 | if (w == null) return null; 425 | @NotNull W nn_w = surely(w); 426 | @Nullable X x = f_W_X.apply(nn_w); 427 | if (x == null) return null; 428 | @NotNull X nn_x = surely(x); 429 | @Nullable Y y = f_X_Y.apply(nn_x); 430 | if (y == null) return null; 431 | @NotNull Y nn_y = surely(y); 432 | return f_Y_Z.apply(nn_y); 433 | } 434 | 435 | /** 436 | * Threads the first parameter through the following function parameters. 437 | *

438 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 439 | * 440 | * @param t Input value, may be null. 441 | * @param fs A typed sequence of functions. 442 | * @return Result or null if the last parameter is a function, otherwise void 443 | */ 444 | public static 445 | @Nullable A let( 446 | @Nullable T t, 447 | Function f_T_U, 448 | Function f_U_V, 449 | Function f_V_W, 450 | Function f_W_X, 451 | Function f_X_Y, 452 | Function f_Y_Z, 453 | Function f_Z_A 454 | ) { 455 | if (t == null) return null; 456 | @NotNull T nn_t = surely(t); 457 | @Nullable U u = f_T_U.apply(nn_t); 458 | if (u == null) return null; 459 | @NotNull U nn_u = surely(u); 460 | @Nullable V v = f_U_V.apply(nn_u); 461 | if (v == null) return null; 462 | @NotNull V nn_v = surely(v); 463 | @Nullable W w = f_V_W.apply(nn_v); 464 | if (w == null) return null; 465 | @NotNull W nn_w = surely(w); 466 | @Nullable X x = f_W_X.apply(nn_w); 467 | if (x == null) return null; 468 | @NotNull X nn_x = surely(x); 469 | @Nullable Y y = f_X_Y.apply(nn_x); 470 | if (y == null) return null; 471 | @NotNull Y nn_y = surely(y); 472 | @Nullable Z z = f_Y_Z.apply(nn_y); 473 | if (z == null) return null; 474 | @NotNull Z nn_z = surely(z); 475 | return f_Z_A.apply(nn_z); 476 | } 477 | 478 | /** 479 | * Threads the first parameter through the following function parameters. 480 | *

481 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 482 | * 483 | * @param t Input value, may be null. 484 | * @param fs A typed sequence of functions. 485 | * @return Result or null if the last parameter is a function, otherwise void 486 | */ 487 | public static 488 | @Nullable B let( 489 | @Nullable T t, 490 | Function f_T_U, 491 | Function f_U_V, 492 | Function f_V_W, 493 | Function f_W_X, 494 | Function f_X_Y, 495 | Function f_Y_Z, 496 | Function f_Z_A, 497 | Function f_A_B 498 | ) { 499 | if (t == null) return null; 500 | @NotNull T nn_t = surely(t); 501 | @Nullable U u = f_T_U.apply(nn_t); 502 | if (u == null) return null; 503 | @NotNull U nn_u = surely(u); 504 | @Nullable V v = f_U_V.apply(nn_u); 505 | if (v == null) return null; 506 | @NotNull V nn_v = surely(v); 507 | @Nullable W w = f_V_W.apply(nn_v); 508 | if (w == null) return null; 509 | @NotNull W nn_w = surely(w); 510 | @Nullable X x = f_W_X.apply(nn_w); 511 | if (x == null) return null; 512 | @NotNull X nn_x = surely(x); 513 | @Nullable Y y = f_X_Y.apply(nn_x); 514 | if (y == null) return null; 515 | @NotNull Y nn_y = surely(y); 516 | @Nullable Z z = f_Y_Z.apply(nn_y); 517 | if (z == null) return null; 518 | @NotNull Z nn_z = surely(z); 519 | @Nullable A a = f_Z_A.apply(nn_z); 520 | if (a == null) return null; 521 | @NotNull A nn_a = surely(a); 522 | return f_A_B.apply(nn_a); 523 | } 524 | 525 | /** 526 | * Threads the first parameter through the following function parameters. 527 | *

528 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 529 | * 530 | * @param t Input value, may be null. 531 | * @param fs A typed sequence of functions. 532 | * @return Result or null if the last parameter is a function, otherwise void 533 | */ 534 | public static 535 | @Nullable C let( 536 | @Nullable T t, 537 | Function f_T_U, 538 | Function f_U_V, 539 | Function f_V_W, 540 | Function f_W_X, 541 | Function f_X_Y, 542 | Function f_Y_Z, 543 | Function f_Z_A, 544 | Function f_A_B, 545 | Function f_B_C 546 | ) { 547 | if (t == null) return null; 548 | @NotNull T nn_t = surely(t); 549 | @Nullable U u = f_T_U.apply(nn_t); 550 | if (u == null) return null; 551 | @NotNull U nn_u = surely(u); 552 | @Nullable V v = f_U_V.apply(nn_u); 553 | if (v == null) return null; 554 | @NotNull V nn_v = surely(v); 555 | @Nullable W w = f_V_W.apply(nn_v); 556 | if (w == null) return null; 557 | @NotNull W nn_w = surely(w); 558 | @Nullable X x = f_W_X.apply(nn_w); 559 | if (x == null) return null; 560 | @NotNull X nn_x = surely(x); 561 | @Nullable Y y = f_X_Y.apply(nn_x); 562 | if (y == null) return null; 563 | @NotNull Y nn_y = surely(y); 564 | @Nullable Z z = f_Y_Z.apply(nn_y); 565 | if (z == null) return null; 566 | @NotNull Z nn_z = surely(z); 567 | @Nullable A a = f_Z_A.apply(nn_z); 568 | if (a == null) return null; 569 | @NotNull A nn_a = surely(a); 570 | @Nullable B b = f_A_B.apply(nn_a); 571 | if (b == null) return null; 572 | @NotNull B nn_b = surely(b); 573 | return f_B_C.apply(nn_b); 574 | } 575 | 576 | 577 | /** 578 | * Threads the first parameter through the following function parameters. 579 | *

580 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 581 | * 582 | * @param t Input value, may be null. 583 | * @param fs A typed sequence of functions. 584 | * @return Result or null if the last parameter is a function, otherwise void 585 | */ 586 | public static 587 | void let( 588 | @Nullable T t, 589 | Consumer c_T 590 | ) { 591 | if (t == null) return; 592 | @NotNull T nn_t = surely(t); 593 | c_T.accept(nn_t); 594 | } 595 | 596 | /** 597 | * Threads the first parameter through the following function parameters. 598 | *

599 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 600 | * 601 | * @param t Input value, may be null. 602 | * @param fs A typed sequence of functions. 603 | * @return Result or null if the last parameter is a function, otherwise void 604 | */ 605 | public static 606 | void let( 607 | @Nullable T t, 608 | Function f_T_U, 609 | Consumer c_U 610 | ) { 611 | if (t == null) return; 612 | @NotNull T nn_t = surely(t); 613 | @Nullable U u = f_T_U.apply(nn_t); 614 | if (u == null) return; 615 | @NotNull U nn_u = surely(u); 616 | c_U.accept(nn_u); 617 | } 618 | 619 | /** 620 | * Threads the first parameter through the following function parameters. 621 | *

622 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 623 | * 624 | * @param t Input value, may be null. 625 | * @param fs A typed sequence of functions. 626 | * @return Result or null if the last parameter is a function, otherwise void 627 | */ 628 | public static 629 | void let( 630 | @Nullable T t, 631 | Function f_T_U, 632 | Function f_U_V, 633 | Consumer c_V 634 | ) { 635 | if (t == null) return; 636 | @NotNull T nn_t = surely(t); 637 | @Nullable U u = f_T_U.apply(nn_t); 638 | if (u == null) return; 639 | @NotNull U nn_u = surely(u); 640 | @Nullable V v = f_U_V.apply(nn_u); 641 | if (v == null) return; 642 | @NotNull V nn_v = surely(v); 643 | c_V.accept(nn_v); 644 | } 645 | 646 | /** 647 | * Threads the first parameter through the following function parameters. 648 | *

649 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 650 | * 651 | * @param t Input value, may be null. 652 | * @param fs A typed sequence of functions. 653 | * @return Result or null if the last parameter is a function, otherwise void 654 | */ 655 | public static 656 | void let( 657 | @Nullable T t, 658 | Function f_T_U, 659 | Function f_U_V, 660 | Function f_V_W, 661 | Consumer c_W 662 | ) { 663 | if (t == null) return; 664 | @NotNull T nn_t = surely(t); 665 | @Nullable U u = f_T_U.apply(nn_t); 666 | if (u == null) return; 667 | @NotNull U nn_u = surely(u); 668 | @Nullable V v = f_U_V.apply(nn_u); 669 | if (v == null) return; 670 | @NotNull V nn_v = surely(v); 671 | @Nullable W w = f_V_W.apply(nn_v); 672 | if (w == null) return; 673 | @NotNull W nn_w = surely(w); 674 | c_W.accept(nn_w); 675 | } 676 | 677 | /** 678 | * Threads the first parameter through the following function parameters. 679 | *

680 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 681 | * 682 | * @param t Input value, may be null. 683 | * @param fs A typed sequence of functions. 684 | * @return Result or null if the last parameter is a function, otherwise void 685 | */ 686 | public static 687 | void let( 688 | @Nullable T t, 689 | Function f_T_U, 690 | Function f_U_V, 691 | Function f_V_W, 692 | Function f_W_X, 693 | Consumer c_X 694 | ) { 695 | if (t == null) return; 696 | @NotNull T nn_t = surely(t); 697 | @Nullable U u = f_T_U.apply(nn_t); 698 | if (u == null) return; 699 | @NotNull U nn_u = surely(u); 700 | @Nullable V v = f_U_V.apply(nn_u); 701 | if (v == null) return; 702 | @NotNull V nn_v = surely(v); 703 | @Nullable W w = f_V_W.apply(nn_v); 704 | if (w == null) return; 705 | @NotNull W nn_w = surely(w); 706 | @Nullable X x = f_W_X.apply(nn_w); 707 | if (x == null) return; 708 | @NotNull X nn_x = surely(x); 709 | c_X.accept(nn_x); 710 | } 711 | 712 | /** 713 | * Threads the first parameter through the following function parameters. 714 | *

715 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 716 | * 717 | * @param t Input value, may be null. 718 | * @param fs A typed sequence of functions. 719 | * @return Result or null if the last parameter is a function, otherwise void 720 | */ 721 | public static 722 | void let( 723 | @Nullable T t, 724 | Function f_T_U, 725 | Function f_U_V, 726 | Function f_V_W, 727 | Function f_W_X, 728 | Function f_X_Y, 729 | Consumer c_Y 730 | ) { 731 | if (t == null) return; 732 | @NotNull T nn_t = surely(t); 733 | @Nullable U u = f_T_U.apply(nn_t); 734 | if (u == null) return; 735 | @NotNull U nn_u = surely(u); 736 | @Nullable V v = f_U_V.apply(nn_u); 737 | if (v == null) return; 738 | @NotNull V nn_v = surely(v); 739 | @Nullable W w = f_V_W.apply(nn_v); 740 | if (w == null) return; 741 | @NotNull W nn_w = surely(w); 742 | @Nullable X x = f_W_X.apply(nn_w); 743 | if (x == null) return; 744 | @NotNull X nn_x = surely(x); 745 | @Nullable Y y = f_X_Y.apply(nn_x); 746 | if (y == null) return; 747 | @NotNull Y nn_y = surely(y); 748 | c_Y.accept(nn_y); 749 | } 750 | 751 | /** 752 | * Threads the first parameter through the following function parameters. 753 | *

754 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 755 | * 756 | * @param t Input value, may be null. 757 | * @param fs A typed sequence of functions. 758 | * @return Result or null if the last parameter is a function, otherwise void 759 | */ 760 | public static 761 | void let( 762 | @Nullable T t, 763 | Function f_T_U, 764 | Function f_U_V, 765 | Function f_V_W, 766 | Function f_W_X, 767 | Function f_X_Y, 768 | Function f_Y_Z, 769 | Consumer c_Z 770 | ) { 771 | if (t == null) return; 772 | @NotNull T nn_t = surely(t); 773 | @Nullable U u = f_T_U.apply(nn_t); 774 | if (u == null) return; 775 | @NotNull U nn_u = surely(u); 776 | @Nullable V v = f_U_V.apply(nn_u); 777 | if (v == null) return; 778 | @NotNull V nn_v = surely(v); 779 | @Nullable W w = f_V_W.apply(nn_v); 780 | if (w == null) return; 781 | @NotNull W nn_w = surely(w); 782 | @Nullable X x = f_W_X.apply(nn_w); 783 | if (x == null) return; 784 | @NotNull X nn_x = surely(x); 785 | @Nullable Y y = f_X_Y.apply(nn_x); 786 | if (y == null) return; 787 | @NotNull Y nn_y = surely(y); 788 | @Nullable Z z = f_Y_Z.apply(nn_y); 789 | if (z == null) return; 790 | @NotNull Z nn_z = surely(z); 791 | c_Z.accept(nn_z); 792 | } 793 | 794 | /** 795 | * Threads the first parameter through the following function parameters. 796 | *

797 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 798 | * 799 | * @param t Input value, may be null. 800 | * @param fs A typed sequence of functions. 801 | * @return Result or null if the last parameter is a function, otherwise void 802 | */ 803 | public static 804 | void let( 805 | @Nullable T t, 806 | Function f_T_U, 807 | Function f_U_V, 808 | Function f_V_W, 809 | Function f_W_X, 810 | Function f_X_Y, 811 | Function f_Y_Z, 812 | Function f_Z_A, 813 | Consumer c_A 814 | ) { 815 | if (t == null) return; 816 | @NotNull T nn_t = surely(t); 817 | @Nullable U u = f_T_U.apply(nn_t); 818 | if (u == null) return; 819 | @NotNull U nn_u = surely(u); 820 | @Nullable V v = f_U_V.apply(nn_u); 821 | if (v == null) return; 822 | @NotNull V nn_v = surely(v); 823 | @Nullable W w = f_V_W.apply(nn_v); 824 | if (w == null) return; 825 | @NotNull W nn_w = surely(w); 826 | @Nullable X x = f_W_X.apply(nn_w); 827 | if (x == null) return; 828 | @NotNull X nn_x = surely(x); 829 | @Nullable Y y = f_X_Y.apply(nn_x); 830 | if (y == null) return; 831 | @NotNull Y nn_y = surely(y); 832 | @Nullable Z z = f_Y_Z.apply(nn_y); 833 | if (z == null) return; 834 | @NotNull Z nn_z = surely(z); 835 | @Nullable A a = f_Z_A.apply(nn_z); 836 | if (a == null) return; 837 | @NotNull A nn_a = surely(a); 838 | c_A.accept(nn_a); 839 | } 840 | 841 | /** 842 | * Threads the first parameter through the following function parameters. 843 | *

844 | * Aborts and returns null when any step in the sequence returns null, otherwise it returns the result of the last function or void if the last parameter is a Consumer. 845 | * 846 | * @param t Input value, may be null. 847 | * @param fs A typed sequence of functions. 848 | * @return Result or null if the last parameter is a function, otherwise void 849 | */ 850 | public static 851 | void let( 852 | @Nullable T t, 853 | Function f_T_U, 854 | Function f_U_V, 855 | Function f_V_W, 856 | Function f_W_X, 857 | Function f_X_Y, 858 | Function f_Y_Z, 859 | Function f_Z_A, 860 | Function f_A_B, 861 | Consumer c_B 862 | ) { 863 | if (t == null) return; 864 | @NotNull T nn_t = surely(t); 865 | @Nullable U u = f_T_U.apply(nn_t); 866 | if (u == null) return; 867 | @NotNull U nn_u = surely(u); 868 | @Nullable V v = f_U_V.apply(nn_u); 869 | if (v == null) return; 870 | @NotNull V nn_v = surely(v); 871 | @Nullable W w = f_V_W.apply(nn_v); 872 | if (w == null) return; 873 | @NotNull W nn_w = surely(w); 874 | @Nullable X x = f_W_X.apply(nn_w); 875 | if (x == null) return; 876 | @NotNull X nn_x = surely(x); 877 | @Nullable Y y = f_X_Y.apply(nn_x); 878 | if (y == null) return; 879 | @NotNull Y nn_y = surely(y); 880 | @Nullable Z z = f_Y_Z.apply(nn_y); 881 | if (z == null) return; 882 | @NotNull Z nn_z = surely(z); 883 | @Nullable A a = f_Z_A.apply(nn_z); 884 | if (a == null) return; 885 | @NotNull A nn_a = surely(a); 886 | @Nullable B b = f_A_B.apply(nn_a); 887 | if (b == null) return; 888 | @NotNull B nn_b = surely(b); 889 | c_B.accept(nn_b); 890 | } 891 | 892 | /** 893 | * Hack to work around the checker issue that ternary operators get ignored in combination with type parameter annotations. 894 | * 895 | * @param t 896 | * @param 897 | * @return 898 | */ 899 | private static 900 | @NotNull T surely(@Nullable T t) { 901 | // workaround for null checker deficiencies 902 | Supplier<@NotNull T> hack = () -> (@NotNull T) Function.identity().apply(t); 903 | return hack.get(); 904 | } 905 | } 906 | 907 | -------------------------------------------------------------------------------- /src/test/java/biz/cosee/null4j/Null4jTest.java: -------------------------------------------------------------------------------- 1 | package biz.cosee.null4j; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | import org.junit.Test; 5 | 6 | import java.util.function.Function; 7 | import java.util.function.Supplier; 8 | 9 | import static biz.cosee.null4j.Null4j.let; 10 | import static biz.cosee.null4j.Null4j.requireNonNullElse; 11 | import static org.hamcrest.core.Is.is; 12 | import static org.junit.Assert.assertNull; 13 | import static org.junit.Assert.assertThat; 14 | 15 | 16 | /** 17 | * Tests let and orDefault. There is also a piece of code that can be used 18 | * to test the annotation manually. 19 | *

20 | * Created by Michael Zinn (@RedNifre), cosee GmbH on 02/05/17. 21 | */ 22 | @NotNullByDefault 23 | public class Null4jTest { 24 | 25 | /** 26 | * You can't really test the annotation automatically, but you can test it manually 27 | * by adding a "/" to the magic comment line below. 28 | */ 29 | /* <- magic comment, set to "//*" to activate test and "/*" to deactivate 30 | public void testAnnotation() { 31 | 32 | // confuse compiler 33 | Supplier hack = () -> null; 34 | 35 | // these should be fine: 36 | String a = "a"; 37 | @Nullable String b = hack.get(); // compiler doesn't know it's really null 38 | 39 | System.out.println(a.length()); 40 | if(b != null) { 41 | // Smart cast: b is now NotNull thanks to the if so this is fine. 42 | System.out.println(b.length()); 43 | } 44 | 45 | // ERROR: c is implicitely @NotNull 46 | // If this is not marked as an error you need to add @NotNullByDefault to the 47 | // class or directly enclosing package. 48 | String c = null; 49 | 50 | // ERROR: Unnecessary null check, a is implied to be @NotNull 51 | if(a != null) { // unnecessary null check 52 | 53 | } 54 | 55 | // ERROR: b is Nullable, needs null check 56 | b.length(); 57 | 58 | // ERROR: last parameter must be NotNull 59 | requireNonNullElse(null, null); 60 | requireNonNullElse(null, b); // compiler doesn't catch that one. 61 | 62 | } 63 | //*/// <- magic comment delimiter, don't change this line. 64 | @Test 65 | public void testRequireNonNullElse() throws Exception { 66 | assertThat( 67 | requireNonNullElse("one", "two"), 68 | is("one") 69 | ); 70 | assertThat( 71 | requireNonNullElse(null, "two"), 72 | is("two") 73 | ); 74 | 75 | 76 | assertThat( 77 | requireNonNullElse("one", "two", "three"), 78 | is("one") 79 | ); 80 | assertThat( 81 | requireNonNullElse(null, "two", "three"), 82 | is("two") 83 | ); 84 | assertThat( 85 | requireNonNullElse(null, null, "three"), 86 | is("three") 87 | ); 88 | 89 | // it should support up to 10 parameters 90 | 91 | assertThat( 92 | requireNonNullElse( 93 | "1", 94 | "2", 95 | "3", 96 | "4", 97 | "5", 98 | "6", 99 | "7", 100 | "8", 101 | "9", 102 | "10" 103 | ), 104 | is("1") 105 | ); 106 | assertThat( 107 | requireNonNullElse( 108 | null, 109 | null, 110 | null, 111 | null, 112 | null, 113 | null, 114 | null, 115 | null, 116 | null, 117 | "10" 118 | ), 119 | is("10") 120 | ); 121 | 122 | /* should not compile: 123 | orDefault(null, null); 124 | */ 125 | } 126 | 127 | @Test 128 | public void testLet() throws Exception { 129 | assertThat( 130 | let("test", String::length), 131 | is(4) 132 | ); 133 | 134 | assertNull(let(null, String::length)); 135 | 136 | assertThat( 137 | let("test", String::length, Object::toString), 138 | is("4") 139 | ); 140 | 141 | 142 | String[] mutable = new String[]{"1"}; 143 | 144 | let("?", o -> { 145 | mutable[0] = "2"; 146 | }); 147 | assertThat(mutable[0], is("2")); 148 | 149 | let(null, o -> { 150 | mutable[0] = "3"; 151 | }); 152 | assertThat(mutable[0], is("2")); 153 | 154 | // it should support up to 10 parameters. 155 | assertThat( 156 | let("chain", 157 | Function.identity(), 158 | Function.identity(), 159 | Function.identity(), 160 | Function.identity(), 161 | Function.identity(), 162 | Function.identity(), 163 | Function.identity(), 164 | Function.identity(), 165 | Function.identity()), 166 | is("chain") 167 | ); 168 | 169 | 170 | let("?", 171 | Function.identity(), 172 | Function.identity(), 173 | Function.identity(), 174 | Function.identity(), 175 | Function.identity(), 176 | Function.identity(), 177 | Function.identity(), 178 | Function.identity(), 179 | o -> { 180 | mutable[0] = "10"; 181 | } 182 | ); 183 | assertThat( 184 | mutable[0], 185 | is("10") 186 | ); 187 | } 188 | 189 | } --------------------------------------------------------------------------------