-
Couldn't load subscription status.
- Fork 11.1k
Description
I'm looking at the changes in 31.0 for enhanced null-checking, and I'm trying to validate that my understanding of the change is correct and bring attention to some issues I am having with the approach.
As an example, let's look at one of the methods that changed from 30 to 31: Iterables.findFirst(). Comparing the signatures in javadoc we have:
- 31:
public static <T extends @Nullable Object> T getFirst(Iterable<? extends T> iterable, T defaultValue) - 30:
public static <T> @Nullable T getFirst(Iterable<? extends T> iterable, @Nullable T defaultValue)
If I understand correctly, nothing has changed in the contract, and in both versions the returned object can be null, the defaultValue can be null and the iterable cannot be null. The issues we are having are as follows:
- This seems fairly hostile to some of the static analysis tools on the market as they have to change their implementation logic to inspect the Generic type of the method/class rather than looking at the parameter or method annotations. We are using Nullaway, and when upgrading to guava 31 previously working code is now broken. While the release notes indeed call this out it's unobvious that this should cause more false positives rather than just identifying more issues because more information is being shared: "By providing additional nullness information, this release may result in more errors or warnings from any nullness analyzers you might use." The issue isn't that we're receiving more nullness information, but that the contract for that how that information is being provided has changed. As an example this code now causes an issue:
@Nullable String value = Iterables.getFirst(ImmutableList.<String>of(), null)because it appears to the static analyzer that second parameter ofIterable#getFirstis null hostile even though this is correct code. - This makes it difficult to read the javadoc for non-static methods like FutureCallback#onSuccess. I can no longer look at this method and understand the nullness convention because there is no information in the method that
Vis actually nullable. I have to look at the class signature itself. Compare this to the old javadoc. Even for static methods, it's unclear that the Nullable actually applies to instances of typeTin the signature as the annotation is on the base typeObject. Is this a common java convention that I just haven't seen before? - It's seems strange in light of the package annotation of ParametersAreNonnullByDefault the definition which calls out that "to indicate that the method parameters in that element are nonnull by default unless there is ... An explicit nullness annotation." It's unobvious that the nullness parameter on the generic type of the method or class count as an explicit nullness annotation (but I am curious whether this was the intent of the JSR authors).
There are a couple other questions I have (how does this convention apply when there is a non-null param and a nullable return, or vice versa) but the ones above are the most pressing.