Hi
Environment
Java 17. SonarQube Developer Edition 9.9 (build 65466) + SonarLint 1.19.3-3 in IntelliJ, in connected mode.
Issue description
S6204
triggers for cases where the code cannot be cleanly simplified due to the method signature being contravariant of the stream elements. For example, see the following method:
public Collection<CharSequence> foo() {
return Stream.of(1, 2)
.map(String::valueOf)
.collect(Collectors.toList());
}
Changing the .collect(...)
into .toList()
yields a compilation error, as generics are invariant. One has to upcast the stream elements like this:
public Collection<CharSequence> foo() {
return Stream.of(1, 2)
.map(String::valueOf)
.map(CharSequence.class::cast)
.toList();
}
Basically, type inference saves us in the first example, but not in the second.
I feel that the second example goes against the spirit of the rule, which states (my emphasis):
In Java 8 Streams were introduced to support chaining of operations over collections in a functional style. The most common way to save a result of such chains is to save them to some collection (usually List). To do so there is a terminal method collect that can be used with a library of Collectors. The key problem is that .collect(Collectors.toList()) actually returns a mutable kind of List while in the majority of cases unmodifiable lists are preferred. In Java 10 a new collector appeared to return an unmodifiable list: toUnmodifiableList(). This does the trick but results in verbose code. Since Java 16 there is now a better variant to produce an unmodifiable list directly from a stream: Stream.toList().
For this specific case I think allowing toUnmodifiableList()
is better than adding another stream operation for the cast. Alternatively, don’t trigger at all in this case.