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?
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.