3

I decided to edit my question, after seeing that 1 year after, I've changed how I work with nulls:

  • I don't use Eclipse builtin null checks, because I found it rather primitive (and perhaps a little tricky to understand)
  • I use @Nullable to tell a value can be null. After all, there should be less null-values than non-null values.
  • I'm using Java 8 and I tend to use Optional, thus allowing the following: Optional.ofNullable(value).orElseGet(() -> 1);. It does not beat the ?: and ?. operator of Groovy, but Optional give some nice tool like map, filter, and so on.

And, as for my code:

  • constructors checks for nulls using Objects.requireNonNull, like this:

    public Foobar(String a) { this.a = Objects.requireNonNull(a, "a"); }

  • methods checks for nulls using Preconditions.checkNotNull from Guava framework whenever I use it in my projects or Objects.requireNonNull:

    public void foobar(String a) { Preconditions.checkNotNull(a, "a"); }

Using the one or the other depends on if I reuse the value.

I don't check for method parameters every time, but rather mostly in the public methods. The idea is not to replace the default runtime check that throws NullPointerException more efficiently than I can do.


I am currently using @Nonnull or @Nullable annotation on all my parameters, fields, method result (return), but I'm wondering what is truly the best :

  • How could I tell that my field and method result are non null by default ? (the @ParameterAreNonnullByDefault does not work for them). I'd like a portable way (I've read here that I could create my own annotations, with specific names, and that would work for findbugs)
  • If I annotate package com.foobar with @ParameterAreNonnullByDefault, does it apply to com.foobar.example as well ?
  • Should I check every parameters (I am currently checking constructor parameters) when annotated by @Nonnull ?

Also, since Eclipse 3.8, there is annotation based null checks. But I have problem with some "simple" case :

@ParameterAreNonnullByDefault
class Foobar<E extends Throwable> {
  @Nullable private Constructor<E> one;
  @Nullable private Constructor<E> two;

  public Foobar(Constructor<E> one, @Nullable Constructor<E> two) {
    this.one = Objects.requireNonNull(one, "one");
    this.two = two;
  }

  // don't care about exceptions.
  public E getInstance(String msg, Throwable t) { 
    if (null == two) {
      return (E)one.newInstance(msg).initCause(t);
    } 
    return two.newInstance(msg, t);
  }
}

Why is telling me that two is nullable at that position, and why he is warning me about potential null access to two ?

Community
  • 1
  • 1
NoDataFound
  • 11,381
  • 33
  • 59
  • 2
    I'm not familiar with the JSR-308 features, but you can't annotate packages; the largest unit you can annotate is a class. – chrylis -cautiouslyoptimistic- Feb 15 '14 at 16:32
  • You can tell a package to be non null by default, it is possible (the annotations permits it). My problem is more : is it inherited from child package (like : should I annotate package com.foobar, or each subpackage com.foobar.example, com.foobar.example2, etc). – NoDataFound Feb 16 '14 at 14:55

3 Answers3

1

As far as the warning on the two variable in getInstance is concerned the null analysis is not clever enough to work out that the field cannot be null. You can work around this by using a local variable:

public E getInstance(String msg, Throwable t) { 
  final Constructor<E> localTwo = two;
  if (null == localTwo) {
    return (E)one.newInstance(msg).initCause(t);
  }
  return localTwo.newInstance(msg, t);
}

There is a setting Enable syntactic null analysis for fields in Preferences > Java > Compiler > Errors/Warnings > Null analysis which allows code like:

if (two != null) {
  return two.newInstance(msg, t);
}

without a warning.

greg-449
  • 109,219
  • 232
  • 102
  • 145
  • ... I don't understand why the (two != null) works, and not null == two if/else. – NoDataFound Feb 16 '14 at 14:59
  • The syntactic check only covers the if statement, it does not cover anything following the if. – greg-449 Feb 16 '14 at 15:14
  • Fine by me, but sad, since I tend to put the clearer statement first (my example is a little more complex, and I have a newInstance method on my class, which in turn handle the Reflection exception). – NoDataFound Feb 16 '14 at 17:27
  • Eclipse has nice IDE features for nullness checking, but as noted by @greg-449, its underlying analysis is somewhat primitive. The Checker Framework's [Nullness Checker](http://types.cs.washington.edu/checker-framework/current/checker-framework-manual.html#nullness-checker) uses a more precise type-based analysis rather than syntactic pattern-matching. It soundly determines that variable `two` in your example is non-null, so it does not issue a warning message. It reads Eclipse's annotations, so you don't have to change your code and you can use both tools. – mernst Feb 27 '15 at 12:58
  • @mernst, do you perform whole system analysis? Otherwise, how could you reason about the value of a variable that the current method doesn't own? Just from looking at the original method getInstance() we'll never know, whether another thread may have assigned `two` with null between the check and the dereference, right? – Stephan Herrmann Feb 28 '15 at 02:39
  • *I* don't perform the analysis; the Checker Framework does. :-) The analysis is modular -- one class at a time. You get a guarantee if you individually verify each part of the whole program (or if you trust some library annotations). A programmer can use annotations to indicate that other threads are not allowed to set the field, or that they are allowed to set the field but not to null. A programmer can also run the tool assuming single-threaded or multi-threaded operation, whichever is appropriate for the program being analyzed. The [manual](http://checkerframework.org/) explains all this. – mernst Feb 28 '15 at 09:35
1

Here's how all your requests can be realized using Eclipse:

Non null by default for fields and method returns

Since Eclipse Kepler, the @NonNullByDefault annotation affects all these locations. Starting with Luna, using Java 8 and type annotations, the effect can be further fine tuned.

As you ask about a portable solution: it's not difficult to use the Eclipse compiler also in CI builds, see the JDT FAQ, and even IntelliJ can be told to use the Eclipse compiler.

Per-Package defaults

Java does not have a concept of sub-packages. If package names share a common prefix this has no semantic implications. Hence, sadly, each package has to be annotated individually.

Once you are ready to apply the default to all packages, Eclipse can be told to warn you / or even raise an error if you missed a default on a package. The option is called: "Missing '@NonNullByDefault' annotation on package".

Runtime checks

In an ideal world, all @NonNull arguments will be fully checked by the compiler and you would not need any runtime checks. It's a matter of how much safety you need, to add these checks, but if you do, I don't see much reason for treating constructor arguments differently from method arguments.

Perhaps, if you have final @NonNull fields, constructors have special responsibility and deserve more checks, but that's basically a matter of your code style and safety requirements.

Unexpected warning in Eclipse

Eclipse performs full flow analysis only for local variables. It is carefully designed this way in order to protect you from the risks of side-effects, aliasing and concurrency, each of which could cause NPE despite the prior null check. The so-called "syntactic analysis" for fields has been introduced as a (limited) compromise, but the real solution must be, to fetch the value into a local variable, where it can be fully analysed. Eclipse even offers a quick fix for this change.

Stephan Herrmann
  • 7,963
  • 2
  • 27
  • 38
  • Well, I switched my way of doing: now I only put `@Nullable`, saying the value "can be null, don't forget to check it", and in other place not null + use `Objects.requireNonNull` (in constructor), `Preconditions.checkNotNull` (from Guava when I have it) in parameters checks. They do the same, but it's purely semantical. – NoDataFound Feb 26 '15 at 21:23
  • It's all mainly a matter of finding a good migration path, and you certainly know best what suits you. In the end I expect you will want to add `@NonNullByDefault` to (almost) all packages (while keeping the explicit `@Nullable` annotations), because that allows the compiler to fully check if your code adheres to your intended contracts - to check it statically, where runtime checks will only be an additional safety net. – Stephan Herrmann Feb 28 '15 at 17:10
  • I extended the answer to cover all four sub-questions. – Stephan Herrmann Feb 28 '15 at 17:53
  • I don't agree that "The real solution must be, to fetch the value into a local variable, where it can be fully analysed." Such a code transformation degrades readability. It is possible for an analysis to check the original code in many circumstances. Eclipse cannot do so, but more fully-featured tools can (one example is the Checker Framework's [Nullness Checker](http://types.cs.washington.edu/checker-framework/current/checker-framework-manual.html#nullness-checker)), and maybe Eclipse will be able to in the future. The refactoring is only a requirement of the current Eclipse implementation. – mernst Mar 01 '15 at 10:49
  • @mernst, I know you don't agree, but you don't show any evidence why the analysis of the Checker Framework can be trusted. Last time I tried (which admittedly is quite some time ago) I was able to easily create an example that the Checker Framework would consider null-safe and which still would throw NPE at runtime. Are you challenging me to create and publish such a counter example? :) – Stephan Herrmann Mar 01 '15 at 22:05
0

I'll give answers to your questions, using an external tool, the Checker Framework's Nullness Checker. It has an Eclipse plug-in and it reads the annotations used by Eclipse and FindBugs. Its analysis is better than the built-in Eclipse nullness analysis, but its IDE integration is not as nice as Eclipse's. Both are worthwhile tools, and you can decide which one is best for you.

How could I tell that my field and method result are non null by default?

That is the default. You don't have to do anything.

If I annotate package com.foobar with @ParameterAreNonnullByDefault, does it apply to com.foobar.example as well?

You don't have to change the defaults at all (though you can, if for some reason you want to use @Nullable as the default in some or all code locations).

Should I check every parameters (I am currently checking constructor parameters) when annotated by @Nonnull ?

You only need to write run-time checks if you are worried that the nullness analysis is unsound — that is, the analysis gives no error even though your code can throw a null pointer exception — or if you are not running the analysis on your entire program. Both Eclipse's and FindBugs's nullness analyses are unsound by design. The Checker Framework is sound by design. Of course, any tool may contain bugs.

Also, since Eclipse 3.8, there is annotation based null checks. But I have problem with some "simple" case:

The Checker Framework handles this simple case precisely -- exactly as you wish it to.

More specifically, the Checker Framework does so when it is checking single-threaded code.

If your code is multi-threaded, then you can supply the -AconcurrentSemantics command-line option. In this case, the Checker Framework will issue a warning because another thread might reset two between the check and the use. If your code is multi-threaded and other threads are modifying fields concurrently, then you probably want to use locks. Copying the value into a local variable, as suggested in another answer, is a bad idea. Not only does it degrades code readability, but it doesn't necessarily behave as you wish; for example, the other thread could violate representation invariants and cause your code to crash in other ways.

mernst
  • 7,437
  • 30
  • 45
  • I'm will try Checker Framework but I can't flag your answer as being the good one. Not that it's bad or so, but I was trying to understand why Eclipse would give me a wrong report and so, the first answer said it: it's primitive, and I need to use cranky local variables for it to work. – NoDataFound Mar 01 '15 at 18:43
  • @mernst, you seem to suggest that Java applications are single-threaded per default. I would rather remind Java programmers that almost all Java programs are multi-threaded in some way or other. – Stephan Herrmann Mar 01 '15 at 22:09
  • @mernst, one more: please don't use labels like "unsound by design" without any justification. TIA – Stephan Herrmann Mar 01 '15 at 22:12
  • @NoDataFound, that is totally reasonable. I was telling you a way to solve your problem, while using Eclipse. But you might prefer some other particular Eclipse plug-in -- it depends on your needs. – mernst Mar 01 '15 at 23:58
  • @Stephan, I didn't say nor suggest that "Java applications are single-threaded per default". I pointed out that the Checker Framework can work in both modes. A user can choose whichever mode is appropriate. That might be a nice feature for Eclipse. – mernst Mar 02 '15 at 00:05
  • @mernst : as its stands today, I'm using Findbugs 3 under Eclipse Luna, since it's well integrated with Eclipse and supports Java 8. I've always though the checker framework to be included in Findbugs. – NoDataFound Mar 02 '15 at 15:11
  • @NoDataFound : FindBugs and the Checker Framework have [different goals](http://types.cs.washington.edu/checker-framework/current/checker-framework-manual.html#choosing-nullness-tool). FindBugs requires less effort from the user, and it finds some nullness bugs in your code but gives to guarantee. The Checker Framework requires more investment from the user (more annatations to write), but it promises to find all the nullness errors. So it's understandable that FindBugs doesn't include a more thorough analysis such as that of the Checker Framework. – mernst Mar 03 '15 at 10:53