Search code examples
javaeclipsegenericsjsr305

@Nullable vs. Generics (Eclipse)


I want to bring a utility method to Java 8. The method named max is defined as follows:

@SafeVarargs
public static final <E extends Comparable<E>> E max(final E... elements) {
    E result = null; // error
    for(final E e : elements) {
        if(result == null || result.compareTo(e) < 0) {
            result = e;
        }
    }
    return result;
}

Now Eclipse shows an error:

Null type mismatch (type annotations): null is not compatible to the free type variable E.

I would have thought that adding @Nullableto the definition of result should remove the error, but this is apparently not the case. Even if I additionally annotate the whole method with @Nullable, the error persists.

By the way I'm using jsr-305.jar from FindBugs and have configured Eclipse to honor javax.annotation.Nonnull as well as javax.annotation.Nullable. Edit: I run Eclipse Luna 4.4.2 and Oracle JDK 8 build 1.8.0_25-b17 on Lubuntu.

Now for my question: Where does the error come from? How do I get rid of it while maintaining the benefit of both the annotation and the generic type definition?

Edit: I just added an example project where the default eclipse annotations are used, and it appears to somewhat work (apart from generating a warning at the result == null because it incorrectly infers that result can only be null at that point). So this appears to be an Eclipse problem with non-default annotation classes. I re-tested it with my own copy of Eclipse's Nullable annotation, and got the same error as above.


Solution

  • Where does the error come from?

    I found this in the documentation:

    A type variable corresponding to an unconstrained type parameter requires pessimistic checking in order to guarantee safety with all legal substitutions: this type can neither be assumed to be nullable nor nonnull.

    class C<T extends Number> {
        int consume(T t) {
            return t.intValue(); // NOT OK since T could be nullable
        }
        T provide() {
            return null;         // NOT OK since T could require nonnull
        }
    } 
    

    The last point may look surprising at first, but please see that an unconstrained type parameter implies that we may not assume anything about the nullness of the type represented by the corresponding type variable. Even more: we must actively support nullable and nonnull types. On the other hand this simply extends the existing rule that the only type being compatible with an unbounded type variable is the type variable itself. To explain this situation in the context of null analysis, the compiler will raise the following error against the return in provide():

    When instantiating a generic type or when invoking a generic method, the constraints put forward by the type parameter must be observed.

    How do I get rid of it while maintaining the benefit of both the annotation and the generic type definition?

    Add @Nullable to <E extends Comparable<E>>, like so

    @SafeVarargs
    public static final  <@Nullable E extends Comparable<E>> E max(final E... elements) {
        E result = null; // no error
        for(final E e : elements) {
            if(result == null || result.compareTo(e) < 0) {
                result = e;
            }
        }
        return result;
    }