178

I can see that @Nullable and @Nonnull annotations could be helpful in preventing NullPointerExceptions but they do not propagate very far.

  • The effectiveness of these annotations drop off completely after one level of indirection, so if you only add a few they don't propagate very far.
  • Since these annotations are not well enforced there is a danger of assuming a value marked with @Nonnull is not null and consequently not performing null checks.

The code below causes a parameter marked with @Nonnull to be null without raising any complaints. It throws a NullPointerException when it is run.

public class Clazz {
    public static void main(String[] args){
        Clazz clazz = new Clazz();

        // this line raises a complaint with the IDE (IntelliJ 11)
        clazz.directPathToA(null);

        // this line does not
        clazz.indirectPathToA(null); 
    }

    public void indirectPathToA(Integer y){
        directPathToA(y);
    }

    public void directPathToA(@Nonnull Integer x){
        x.toString(); // do stuff to x        
    }
}

Is there a way to make these annotations more strictly enforced and/or propagate further?

Sled
  • 18,541
  • 27
  • 119
  • 168
Rylander
  • 19,449
  • 25
  • 93
  • 144
  • 1
    I like the idea of `@Nullable` or `@Nonnull`, but if they are worth it is very "likely to solicit debate" – Maarten Bodewes Nov 20 '12 at 23:59
  • I think the way to move to a world where this causes a compiler error or warning is to require a cast to `@Nonnull` when calling an `@Nonnull` method with a nullable variable. Of course, casting with an annotation is not possible in Java 7, but Java 8 will be adding the ability to apply annotations to the use of a variable, including casts. So this may become possible to implement in Java 8. – Theodore Murdock Mar 10 '14 at 22:23
  • 2
    @TheodoreMurdock, yes, in Java 8 a cast `(@NonNull Integer) y` is syntactically possible, but a compiler is not allowed to emit any specific byte code based on the annotation. For runtime assertions tiny helper methods are sufficient as discussed in https://bugs.eclipse.org/442103 (e.g., `directPathToA(assertNonNull(y))`) - but mind you, this only helps to fail fast. The only safe way is by performing an actual null check (plus hopefully an alternative implementation in the else branch). – Stephan Herrmann Sep 18 '15 at 22:13
  • @StephanHerrmann Hmm, disappointing that a null check can't be emitted for the assertion to automatically fail fast without the helper method. I am happy to see from your new answer that the other piece has benefitted from the ability to perform a cast: it is now somewhat lower cost to have our compilers emit an 'unchecked conversion' warning when we fail to assert that a value is `@NonNull` when calling a method that takes an `@NonNull` parameter, because we now have an easier way to make that assertion when necessary. – Theodore Murdock Sep 21 '15 at 18:20
  • 3
    It would be helpful in this question to say which ```@Nonnull``` and ```@Nullable``` you are talking about, as there are multiple similar annoations (See **[this question](http://stackoverflow.com/questions/35892063/which-nonnull-java-annotation-to-use)**). Are you talking about the annotations in package ```javax.annotation```? – James Dunn Feb 02 '17 at 15:46
  • 1
    @TJamesBoone For the context of this question it does not matter, this was about how to use any of them effectively. – Rylander Feb 06 '17 at 17:42
  • I don't mind writing the seemingly redundant and unnecessary "void foo(@NonNull Object param1) { if (param1 == null) throw IllegalArgumentException('param1 must not be null') ... }" to my code where appropriate, but my problem with this that the default lint rules flag the "if" check as unnecessary, where at runtime it really is necessary, and I don't trust that disabling the rule wouldn't have undesired side effects somewhere else in my code. Anyone have clean suggestions? I'd love to implement the @NonNull annotation to throw a runtime exception if passed a null value during runtime. – swooby Mar 15 '17 at 17:54
  • nulls are just plain wrong. The only reason to use nulls in a program according to "todays" standards is because we are receiving a pontentially nullable variable from a old (deprecated) library. The null must never "scape" a few lines of code, just to check if it's null and asign a proper value or throw an exception, and of course it must never be propagated outside of a local block of code or level of indirection. – earizon Jun 14 '17 at 07:52
  • related https://stackoverflow.com/questions/4963300 – tkruse Jul 28 '18 at 01:16
  • There is the concept of fail-fast, in that if @NonNull is ignored, you *want* an exception to be thrown, since you are declaring that null parameter is invalid at any time. – Brill Pappin Jun 04 '19 at 20:22
  • Coming from Swift and learning Java. It's like going VERY far back in time. – William Entriken Aug 15 '19 at 14:10
  • @WilliamEntriken If you have the option, you can mix Kotlin in with Java. Kotlin is very interoperable with Java and has a lot of the niceties you expect in Swift... also solves this problem https://stackoverflow.com/a/46290602/444639 – Rylander Aug 15 '19 at 19:02

11 Answers11

87

Short answer: I guess these annotations are only useful for your IDE to warn you of potentially null pointer errors.

As said in the "Clean Code" book, you should check your public method's parameters and also avoid checking invariants.

Another good tip is never returning null value and use Null Object Pattern instead.

Pedro Boechat
  • 2,446
  • 1
  • 20
  • 25
  • 19
    For return values possibly being empty, I strongly suggest using the `Optional` type instead of plain `null` – Patrick Feb 03 '18 at 13:57
  • 12
    Optional ist not better, than "null". Optional#get() throws NoSuchElementException while a usage of the null throws NullPointerException. Both are RuntimeException's without meaningful description. I prefer nullable variables. – 30thh Sep 07 '18 at 11:38
  • 8
    @30thh why would you use Optional.get() directly and not Optional.isPresent() or Optional.map first? – GauravJ Sep 21 '18 at 00:45
  • 16
    @GauravJ Why would you use a nullable variable directly and not check, if it null first? ;-) – 30thh Sep 25 '18 at 09:43
  • 16
    The difference between `Optional` and nullable in this case is that `Optional` better communicates that this value can intentionally be empty. Certainly, it's not a magic wand and in the runtime it may fail exactly the same way as nullable variable. However, API reception by programmer is better with `Optional` in my opinion. – user1053510 Jan 25 '19 at 07:14
  • Why not create a static class instead of an extra `Optional` object? – Shukant Pal Feb 04 '19 at 22:02
  • Thanks for remainding me the Null Object pattern – Laurent D. Nov 21 '20 at 15:50
  • Because 'null' is an everything type. And you can only use Optional as an optional. Optionals don't give you NPEs. – starflyer Dec 02 '21 at 20:50
  • 1
    @30thh the advantage of Optionals compared to null references is that the Optional class forces you to think about the case when the value is not present. As a consequence, you can prevent unintended null pointer exceptions. Where as null values can sneak up on you in production. https://www.oracle.com/technical-resources/articles/java/java8-optional.html – Ghos3t Feb 22 '23 at 21:20
  • My answer to this: "@GauravJ Why would you use a nullable variable directly and not check, if it null first? ;-)" - You can easily forget to do the null check, while Optional reminds/forces you to do it. Good read here: https://www.oracle.com/technical-resources/articles/java/java8-optional.html – Trayan Momkov Mar 02 '23 at 14:39
39

Other than your IDE giving you hints when you pass null to methods that expect the argument to not be null, there are further advantages:

This can help your code be more maintainable (since you do not need null checks) and less error-prone.

Brad Turek
  • 2,472
  • 3
  • 30
  • 56
Uwe Plonus
  • 9,803
  • 4
  • 41
  • 48
  • 11
    I sympathize with the OP here, because even though you cite these two advantages, in both cases you used the word "can." That means that there is no guarantee that these checks will actually occur. Now, that behavioral difference could be useful for performance-sensitive tests that you'd like to avoid running in production mode, for which we have `assert`. I find `@Nullable` and `@Nonnull` to be useful ideas, but I'd like more force behind them, rather than us hypothesizing about what one *could* do with them, which still leaves open the possibility of doing nothing with them. – seh Nov 21 '12 at 13:34
  • 2
    The question is where to begin. At the moment his anntations are optional. Somtimes I would like it if they were not because in some circumstances it would be helpful to enforce them... – Uwe Plonus Nov 21 '12 at 13:52
  • May I ask what is AOP you're referring here? – Chris.Zou Mar 14 '15 at 03:53
  • @Chris.Zou AOP means aspect oriented programming, e.g. AspectJ – Uwe Plonus Mar 14 '15 at 11:45
17

I think this original question indirectly points to a general recommendation that run-time null-pointer check is still needed, even though @NonNull is used. Refer to the following link:

Java 8's new Type Annotations

In the above blog, it is recommended that:

Optional Type Annotations are not a substitute for runtime validation Before Type Annotations, the primary location for describing things like nullability or ranges was in the javadoc. With Type annotations, this communication comes into the bytecode in a way for compile-time verification. Your code should still perform runtime validation.

GOTO 0
  • 42,323
  • 22
  • 125
  • 158
jonathanzh
  • 1,346
  • 15
  • 21
  • 1
    Understood, but the default lint checks warn that runtime null checks are unnecessary, which at first impression seems to discourage this recommendation. – swooby Mar 15 '17 at 18:10
  • 2
    @swooby Usually I ignore lint warnings if I’m sure my code is correct. Those warnings are not errors. – jonathanzh Mar 16 '17 at 21:34
13

Compiling the original example in Eclipse at compliance 1.8 and with annotation based null analysis enabled, we get this warning:

    directPathToA(y);
                  ^
Null type safety (type annotations): The expression of type 'Integer' needs unchecked conversion to conform to '@NonNull Integer'

This warning is worded in analogy to those warnings you get when mixing generified code with legacy code using raw types ("unchecked conversion"). We have the exact same situation here: method indirectPathToA() has a "legacy" signature in that it doesn't specify any null contract. Tools can easily report this, so they will chase you down all alleys where null annotations need to be propagated but aren't yet.

And when using a clever @NonNullByDefault we don't even have to say this every time.

In other words: whether or not null annotations "propagate very far" may depend on the tool you use, and on how rigorously you attend to all the warnings issued by the tool. With TYPE_USE null annotations you finally have the option to let the tool warn you about every possible NPE in your program, because nullness has become an intrisic property of the type system.

Stephan Herrmann
  • 7,963
  • 2
  • 27
  • 38
8

What I do in my projects is to activate the following option in the "Constant conditions & exceptions" code inspection:
Suggest @Nullable annotation for methods that may possibly return null and report nullable values passed to non-annotated parameters Inspections

When activated, all non-annotated parameters will be treated as non-null and thus you will also see a warning on your indirect call:

clazz.indirectPathToA(null); 

For even stronger checks the Checker Framework may be a good choice (see this nice tutorial.
Note: I have not used that yet and there may be problems with the Jack compiler: see this bugreport

TmTron
  • 17,012
  • 10
  • 94
  • 142
7

I agree that the annotations "don't propagate very far". However, I see the mistake on the programmer's side.

I understand the Nonnull annotation as documentation. The following method expresses that is requires (as a precondition) a non-null argument x.

    public void directPathToA(@Nonnull Integer x){
        x.toString(); // do stuff to x        
    }

The following code snippet then contains a bug. The method calls directPathToA() without enforcing that y is non-null (that is, it does not guarantee the precondition of the called method). One possibility is to add a Nonnull annotation as well to indirectPathToA() (propagating the precondition). Possibility two is to check for the nullity of y in indirectPathToA() and avoid the call to directPathToA() when y is null.

    public void indirectPathToA(Integer y){
        directPathToA(y);
    }
seh
  • 14,999
  • 2
  • 48
  • 58
Andres
  • 89
  • 1
  • 3
  • 1
    Propagating the `@Nonnull` to have `indirectPathToA(@Nonnull Integer y)` is IMHO a bad practice: you will need to mainain the propagation on the full call stack (so if you add a `null` check in `directPathToA()`, you will need to replace `@Nonnull`by `@Nullable` in the full call stack). This would be a huge maintenance effort for large applications. – Julien Kronegg Jul 22 '16 at 13:39
  • 3
    @Nonnull annotation just emphasises that null-verification of the argument is on your side (you have to guarantee that you pass non-null value). It is not the responsibility of the method. – Alexander Drobyshevsky May 29 '17 at 14:45
  • 1
    @Nonnull is also sensible thing when null-value do not make any sense for this method – Alexander Drobyshevsky May 29 '17 at 14:47
7

If you use Kotlin, it supports these nullability annotations in its compiler and will prevent you from passing a null to a Java method that requires a non-null argument. Even though this question was originally targeted at Java, I mention this Kotlin feature because it is specifically targeted at these Java annotation and the question was "Is there a way to make these annotations more strictly enforced and/or propagate further?" and this feature does make these annotation more strictly enforced.

Java class using @NotNull annotation:

public class MyJavaClazz {
    public void foo(@NotNull String myString) {
        // will result in an NPE if myString is null
        myString.hashCode();
    }
}

Kotlin class calling Java class and passing null for the argument annotated with @NotNull:

class MyKotlinClazz {
    fun foo() {
        MyJavaClazz().foo(null)
    }
}  

Kotlin compiler error enforcing the @NotNull annotation:

Error:(5, 27) Kotlin: Null can not be a value of a non-null type String

See: https://kotlinlang.org/docs/java-interop.html#nullability-annotations

Pang
  • 9,564
  • 146
  • 81
  • 122
Rylander
  • 19,449
  • 25
  • 93
  • 144
  • 3
    The question addresses Java, per its first tag, and not Kotlin. – seh Sep 19 '17 at 12:51
  • 2
    @seh see update for why this answer is relevant to this question. – Rylander Sep 20 '17 at 16:20
  • 2
    Fair enough. That's a nice feature of Kotlin. I just don't think it's going to satisfy those coming here to learn about Java. – seh Sep 20 '17 at 16:29
  • but accessing `myString.hashCode()` will still be throwing NPE even if `@NotNull` is not added in parameter. So whats more specific about adding it? – kAmol Oct 17 '18 at 04:37
  • @kAmol The difference here is that when using Kotlin, you will get a *compile* time error instead of a *runtime* error. The annotation is to notify that you the developer need to ensure that a null is not passed in. This will not prevent a null from being passed in at runtime, but it will prevent you from writing code that calls this method with a null (or with a function that can return null). – Rylander Oct 18 '18 at 19:11
4

In Java I'd use Guava's Optional type. Being an actual type you get compiler guarantees about its use. It's easy to bypass it and obtain a NullPointerException, but at least the signature of the method clearly communicates what it expects as an argument or what it might return.

Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
  • 16
    You have to be careful with this. Optional should only be used where a value is truly optional, and the absence of which is used as a decision gate for further logic. I've seen this abused with blanket replacement of Objects with Optionals and null checks replaced with checks for presence which misses the point. – Christopher Perry Jan 11 '14 at 04:20
  • if you are targeting JDK 8 or newer, prefer to use `java.util.Optional` instead of Guava's class. [See Guava's notes/comparison](https://guava.dev/releases/28.1-jre/api/docs/com/google/common/base/Optional.html) for details on the differences. – AndrewF Oct 23 '19 at 19:39
  • 1
    "null checks replaced with checks for presence which misses the point" can you elaborate, then, on what the point *is*? This isn't the *only* reason for Optionals, in my opinion, but it's certainly by far the largest and best one. – scubbo Jan 07 '20 at 18:13
0

@NotNull from javax.validation.constraints means that the method never returns a null value.. ;) So, that means on getting any value from that method, you need not add any null check for checking for null pointer exceptions.. :D

0

A very old question, but I don't see anybody pointing out that to get a check on execution time you should use assertions.

Like this

assert x != null;

This will work (throw an exception) no matter the level of indirection

ilomambo
  • 8,290
  • 12
  • 57
  • 106
  • This is true, but an NPE would already be thrown... The answer I eventually went with is to use Kotlin wherever possible and then get this as a compile time check. See: https://stackoverflow.com/a/46290602/444639 – Rylander Apr 24 '23 at 00:00
-6

Since Java 8 new feature Optional you should not use @Nullable or @Notnull in your own code anymore. Take the example below:

public void printValue(@Nullable myValue) {
    if (myValue != null) {
        System.out.print(myValue);
    } else {
        System.out.print("I dont have a value");
}

It could be rewritten with:

public void printValue(Optional<String> myValue) {
    if (myValue.ifPresent) {
        System.out.print(myValue.get());
    } else {
        System.out.print("I dont have a value");
}

Using an optional forces you to check for null value. In the code above, you can only access the value by calling the get method.

Another advantage is that the code get more readable. With the addition of Java 9 ifPresentOrElse, the function could even be written as:

public void printValue(Optional<String> myValue) {
    myValue.ifPresentOrElse(
        v -> System.out.print(v),
        () -> System.out.print("I dont have a value"),
    )
}
César Alforde
  • 2,028
  • 2
  • 15
  • 17
Mathieu Gemard
  • 514
  • 7
  • 11
  • Even with `Optional`, there are still many libraries and frameworks that use these annotations such that it is not feasible to update/replace all of your dependencies with versions updated to use Optionals. `Optional` can however help in situations where you use null within your own code. – Rylander Dec 26 '19 at 17:00