I have a class which derives from java.util.concurrent.AbstractExecutorService
. My class overrides shutdownNow()
. Everything compiles and runs fine.
I added org.eclipse.jdt.annotation.@NonNullByDefault
to the class. I managed to fix all of the errors and warnings except for 1 error on shutdownNow()
. The error message says...
The return type is incompatible with 'List' returned from ExecutorService.shutdownNow() (mismatching null constraints)
The quick fixes aren't any help.
Here's the offending code.
@NonNullByDefault // Adding this causes an error
public abstract class ShutdownThreadPool extends AbstractExecutorService implements ExecutorService
{
@Override
public List<Runnable> shutdownNow() // The error is on this line
{
return(Collections.emptyList());
}
}
Note: Collections.emptyList() isn't the problem. It is just a simplification of the the actual code which reproduces the same error message.
Here's an image of the code.
It seems that ExecutorService
is not in the scope of any @NonNullByDefault
, right?
Furthermore, ExecutorService
declares List<Runnable> shutdownNow()
.
The override, however, is affected by @NonNullByDefault
making its effective signature @NonNull List<@NonNull Runnable> shutdownNow()
.
Unfortunately, the quick fix only regards the first @NonNull
, whereas the annotation on the type argument Runnable
is actually causing the incompatibility:
We don't know the intended semantics of ExecutorService.shutdownNow()
: should clients expect nullable or nonnull elements in the returned list? Assuming List<@NonNull Runnable>
would break potential callers of shutdownNow()
that like to insert null
into the result list (ignoring the strange design this would imply).
To make this override type safe, one should first introduce null annotations on the superinterface (in this case using external annotations), and then let implementations follow suite.
If annotating the superinterface is not viable for any reason, i.e., continuing with the "legacy" type List<Runnable>
is desired, then the null default can be cancelled for this one method by saying @NonNullByDefault({})
, and manually adding @NonNull
where it is still desired.