I have a code example where Java forces me to use the extends keyword on a class that is final in a PECS construction. I do not understand why exactly. The code can be found below or at https://onecompiler.com/java/3yvf4g97f. The compiler fails to compile method named process. If I remove method process then SonarLint will trigger a violation of rule https://rules.sonarsource.com/java/RSPEC-4968 for method named processPecs. Which seems reasonable, or is this a false positive? Is there a better approach for "processing arbitrary data"? Do I need to suppress SonarLint's warning here and file a bug report for SonarLint?
import java.util.List;
class PecsTest {
static final class DataContainer<D> {
final D data;
public DataContainer(D data) {
this.data = data;
}
D getData() {
return data;
}
}
static class Processor<D> {
@SuppressWarnings("unchecked")
List<DataContainer<D>> processPecs(List<? extends DataContainer<? super D>> list) {
return (List<DataContainer<D>>) list;
}
@SuppressWarnings("unchecked")
List<DataContainer<D>> process(List<DataContainer<? super D>> list) {
return (List<DataContainer<D>>) list;
}
}
static class Data {
}
static class ExtendedData extends Data {
}
public static void main(String[] args) {
new Processor<Data>().processPecs(List.of(new DataContainer<>(new ExtendedData())));
new Processor<ExtendedData>().processPecs(List.of(new DataContainer<>(new Data())));
}
}
A
DataContainer<ExtendedData>is not assignable toDataContainer<Data>. But sinceDataContaineris a producer, it makes sense to use? extends Datahere (the PE in PECS), as both types can produce a value of typeData:In other words, both,
DataContainer<ExtendedData>andDataContainer<Data>, are subtypes ofDataContainer<? extends Data>. The fact that the classDataContainerisfinalis irrelevant.So, the Sonar warning is wrong here. Suggesting to avoid final classes as upper bound only makes sense if the final class is not generic.
The same reasoning about
DataContainer<ExtendedData>not being assignable toDataContainer<Data>despiteExtendedDatais a subclass ofDataapplies to aListof generic types.DataContainer<ExtendedData>is a subtype ofDataContainer<? extends Data>but aList<DataContainer<ExtendedData>is not assignable toList<DataContainer<? extends Data>>.You have to resort to
? extendswhen the list acts as a producer, i.e. allows you to getDataContainer<? extends Data>elements from it.Note that since
DataContainer<Data>andDataContainer<ExtendedData>are distinct types, neither being a subtype of the other, there is no way to define a common consumer type forList<DataContainer<Data>>andList<DataContainer<ExtendedData>>that allows to add elements of both types.Keep in mind that with invocation type inference, results may seem to contradict these rules at the first glance. For example, when you write
you get no error. Not because you can add a
DataContainer<ExtendedData>to aList<DataContainer<Data>but because in this case, the compiler inferredDataContainer<Data>as the type to construct. And passing an instance ofExtendedDatato the constructor ofDataContainer<Data>is valid.