javanullannotations

Handling null, even if annotations are used


I started using javax.annotation especially to warn the next developer who maybe will be working with my code in the future.

But while I was using the javax.annotation @Nonnull annotation, a question came into my mind:

If you mark f.e. a parameter of a method through the @Nonnull annotation that it has to have a value, do you still need to handle the case, that the next developer who is using your code provides null to your function?

If found one con argument and one pro argument to still handle the special cases.

con: The code is cleaner, especially if you have multiple parameters that you mark with @Nonnull

private void foo(@Nonnull Object o) {
    /* do something */
}

vs

public void foo(Object o) throws NullPointerException {
    if (o == null) {
        throw new NullPointerException("Given Object must have a value!");
    }
    /* do something */
}

pro: It could cause unhandled errors if the next developer ignore the annotations.


Solution

  • This is an unsolved problem in the nullity annotation space. There are 2 viewpoints that sound identical but result, in fact, in the exact opposite. Given a parameter void foo(@NonNull String param), what does that imply?

    Crucially, then, the latter means a null check is flagged as silly code, whereas the former means lack of a null check is marked as bad. Hence, opposites. The former considered a nullcheck a warnable offense (with something along the lines of param can never be null here), for the same reason this is silly code:

    void foo(String arg) {
      if (!(arg instanceof String)) throw new IllegalArgumentException("arg");
    }
    

    That if clause cannot possibly fire. The mindset of various nullchecker frameworks is identical here, and therefore flags it as silly code:

    void foo(@NonNull String arg) {
      if (arg == null) throw new NullPointerException("arg");
    }
    

    The simple fact is, plenty of java devs do not enable annotation-based nullity checking, and even if they did, there are at least 10 competing annotations and many of them mean completely different things, and work completely differently. The vast majority will not be using a checking framework that works as you think it should, therefore, the advice to remove the nullcheck because it is silly is actively a bad thing - you should add that nullcheck. The linting tools that flag this down are misguided; they want to pretend to live in a world where every java programmer on the planet uses their tool. This isn't true and is unlikely to ever become true, hence, wrong.

    A few null checking frameworks are sort of living both lives and will allow you to test if an argument marked as @NonNull is null, but only if the if body starts with throw, otherwise it's flagged.

    To answer your questions:

    if (param1 == null) throw new NullPointerException("param1");
    if (param2 == null) throw new NullPointerException("param2");
    

    is far more legible, especially considering this method has more lines than just those two, than this:

    if (param1 == null) {
      throw new NullPointerException("param1");
    }
    
    if (param2 == null) {
      throw new NullPointerException("param2");
    }
    

    Styleguides are just a tool. If your styleguide is leading to less productivity and harder to read code, the answer should be obvious. Fix or replace the tool.