Another way to help inference, in cases like this would be to break up the method chain - as follows:

final Stream<Collection<String>> stream = Stream.of(nestedDequeue, nestedList);
final List<Collection<String>> list = stream.toList();

In the above rewriting, now the target type Stream<Collection<String>> "drives" inference for Stream::of - meaning that inference will pick the "correct" supertype.

The problem with the original example is that the target type applies to the Stream::toList() call, and the call to Stream::of is type-checked in a bottom-up fashion, w/o any influence from the target-type.

This is, unfortunately, a known limitation of the inference scheme specified in the JLS and implemented by javac.

So, in a way, both the `var` example and this one are similar in spirit, as they both try to compute the least upper bound between two collections, in a situation where no target type is available: in one case, trivially, because the target is `var`; in the other case because the target doesn't "flow" to the method call that would require it the most.

Maurizio

On 04/05/2023 14:23, Raffaello Giulietti wrote:
Without changing the semantics at all, you could also write

    final List<Collection<String>> list = Stream.<Collection<String>>of(nestedDequeue, nestedList).toList();

to "help" type inference.




On 2023-05-03 15:12, fo...@univ-mlv.fr wrote:
Another example sent to me by a fellow French guy,

     final Deque<String> nestedDequeue = new ArrayDeque<>();
     nestedDequeue.addFirst("C");
     nestedDequeue.addFirst("B");
     nestedDequeue.addFirst("A");

     final List<String> nestedList = new ArrayList<>();
     nestedList.add("D");
     nestedList.add("E");
     nestedList.add("F");

     final List<Collection<String>> list = Stream.of(nestedDequeue, nestedList).toList();

This one is cool because no 'var' is involved and using collect(Collectors.toList()) instead of toList() solves the inference problem.

Rémi

----- Original Message -----
From: "Stuart Marks" <stuart.ma...@oracle.com>
To: "Remi Forax" <fo...@univ-mlv.fr>
Cc: "core-libs-dev" <core-libs-...@openjdk.java.net>
Sent: Tuesday, May 2, 2023 2:44:28 AM
Subject: Re: The introduction of Sequenced collections is not a source compatible change

Hi Rémi,

Thanks for trying out the latest build!

I'll make sure this gets mentioned in the release note for Sequenced
Collections.
We'll also raise this issue when we talk about this feature in the Quality
Outreach
program.

s'marks

On 4/29/23 3:46 AM, Remi Forax wrote:
I've several repositories that now fails to compile with the latest jdk21, which
introduces sequence collections.

The introduction of a common supertype to existing collections is *not* a source
compatible change because of type inference.

Here is a simplified example:

    public static void m(List<Supplier<? extends Map<String, String>>> factories) {
    }

    public static void main(String[] args) {
      Supplier<LinkedHashMap<String,String>> supplier1 = LinkedHashMap::new;
      Supplier<SortedMap<String,String>> supplier2 = TreeMap::new;
      var factories = List.of(supplier1, supplier2);
      m(factories);
    }


This example compiles fine with Java 20 but report an error with Java 21:     SequencedCollectionBug.java:28: error: method m in class SequencedCollectionBug
    cannot be applied to given types;
      m(factories);
      ^
    required: List<Supplier<? extends Map<String,String>>>
    found:    List<Supplier<? extends SequencedMap<String,String>>>
    reason: argument mismatch; List<Supplier<? extends SequencedMap<String,String>>>     cannot be converted to List<Supplier<? extends Map<String,String>>>



Apart from the example above, most of the failures I see are in the unit tests provided to the students, because we are using a lot of 'var' in them so they
work whatever the name of the types chosen by the students.

Discussing with a colleague, we also believe that this bug is not limited to
Java, existing Kotlin codes will also fail to compile due to this bug.

Regards,
Rémi

Reply via email to