java:S6204 should not apply when method signature is contravariant

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.

Hey there.

Thanks for the report. This false-positive has already been reported and you can track it here! [SONARJAVA-4422] - Jira

1 Like

Thanks for the reply! I think the linked issue is slightly different from the one above: the linked one deals with the case where the list is actually modified, the one above leads to a compilation error due to the type system.

Thanks. I’m far away from being able to understand this level of nuance, so I’ve pinged some experts!

1 Like

Hello Andreas,

Thanks for the report, interesting case. Indeed, we missed this one while implementing the rule and I agree with you that the second example is not in line with the spirit of the rule. I’ve created a ticket to address this case during our next Java analyzer hardening efforts.

1 Like

Awesome, thanks!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.