Eclipse null analysis issues null type safety warn

2019-08-09 08:00发布

问题:

Consider the following partial content of an Eclipse project that has null analysis enabled:

Eclipse Mars Release (4.5.0; 20150621-1200) for Windows 64-bit
Oracle JDK 1.8.0_60

package-info.java

@org.eclipse.jdt.annotation.NonNullByDefault
package bar;

Foo.java

package bar;

public class Foo {
    public static void main(String[] args) {
    }
}

class Base<T> {
    private final Class<T> type;

    public Base(Class<T> type) {
        this.type = type;
    }

    public Class<T> getType() {
        return this.type;
    }
}

class Derived extends Base<String> {
    public Derived() {
        super(String.class); // <-- Null type safety warning here
    }
}

Null analysis produces the following warning in the call to the superclass ctor in Derived:

Null type safety (type annotations): The expression of type 'Class<String>' needs unchecked conversion to conform to '@NonNull Class<@NonNull String>'

What is the correct way to avoid this warning?

回答1:

The conflict you are seeing is between the type arguments ...

  • ... of the required type: @NonNull String
  • ... of the provided type: String

The non-null type argument comes into the picture from extends Base<String>, which under the regime of @NonNullByDefault is expanded to extends Base<@NonNull String>. From here it percolates all the way up to the super constructor, to render its signature after type argument substitution as (@NonNull Class<@NonNull String>). By contrast, the class literal is seen by the compiler as having type @NonNull Class<String>, which is not compatible.

The real solution will come via Bug 477719, see in particular the discussion in comment 3 f.

For the time being (with Eclipse Mars) you may have to reduce the effect of @NonNullByDefault so that @NonNull Class<String> will be accepted. This can be achieved by a more specific @NNBD declaration on top of class Derived like this

import static org.eclipse.jdt.annotation.DefaultLocation.*;
...
@NonNullByDefault({PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND})
class Derived extends Base<String> {
...

Here we are leveraging the fact, the @NNBD can be fine tuned regarding the locations to which it applies. In this declaration the difference to an argument-less @NonNullByDefault lies in omitting the location TYPE_ARGUMENT, which means that String in extends Base<String> will no longer be affected like mentioned above. Ergo the super constructor will now have this signature: (@NonNull Class<String>) and all compiles without warnings.

By putting the fine tuned @NNBD on the class Derived the scope of this workaround is kept a small as possible.