[External] : The introduction of Sequenced collections is not a source compatible change

Stuart Marks stuart.marks at oracle.com
Thu May 4 22:45:51 UTC 2023


The introduction of Sequenced Collections in JDK 21+20 did make a difference in this 
specific case. One can compile Rémi's example on JDK 21+19 successfully, but it will 
fail on JDK 21+20.

Here's Rémi's 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);
     }

The type of 'factories' is inferred to be a List of the /something/ which is the 
common supertype (least upper bound, or LUB) of the arguments. It's pretty easy to 
see this in an IDE or in jshell. In JDK 21+19 factories is inferred to have this type:

     List<Supplier<? extends Map<String,String>>>

whereas in JDK 21+20 it's inferred to have this type:

     List<Supplier<? extends SequencedMap<String,String>>>

Rémi's example carefully defined the parameter of the m() method so that it matches 
the first (and compiles successfully) but does not match the second (and so fails 
with a compilation error).

This isn't a bug in type inference or in Sequenced Collections. It's a consequence 
of a few phenomena: 1) the result of type inference depends on the exact inputs 
given; 2) the result of type inference depends on the current type hierarchy; 3) 
it's possible to write source code that implicitly depends on a specific result of 
type inference. Sequenced Collections changed the type hierarchy, so inference gave 
different results, and the example depended on getting a specific result under JDK 
21+19.

When you changed the code to List.of(supplier1), this changed the inputs to type 
inference, so it got a different result:

List<Supplier<LinkedHashMap<String,String>>>

This doesn't match the parameter of the m() method, so this gives a compilation 
error on all versions of the system.

As you observed, changing the declaration of m() to be:

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

This lets the examples compile on JDK 21+19 and JDK 21+20, whether List.of() is 
given one or both arguments. This change broadens the set of types accepted by m(), 
reducing its specific dependency on a specific arrangement of the type hierarchy. 
It's also probably a good idea in general, as we recommend using wildcard variance 
(? extends T) consistently in order to increase the flexibility of methods. For 
example, I might have a MySupplier functional interface that adds some features to 
the built-in Supplier interface:

     interface MySupplier<T> extends Supplier<T> {
         // more features
     }

If the suppliers were declared as instances of MySupplier instead, Rémi's example 
would fail even on JDK 21+19. So yes, in a sense, this doesn't have much to do with 
the introduction of Sequenced Collections.

s'marks



On 5/2/23 5:09 AM, Blaise B. wrote:
>
> Hello,
>
> I've tested the example provided by Rémi, and it looks like the compile-time error 
> has little to do with the integration of Sequenced Collections into mainline.
> By changing the "factories" list to contain only one element instead of two, the 
> code does not compile even in previous jdk versions (tested on jdk 17, 18, 19, 20, 
> 21):
>
>        import java.util.*;
>        import java.util.function.Supplier;
>
>        public class SequencedCollectionsTest {
>
>              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);
>                        var factories = List.of(supplier1);
>
>                        m(factories);
>              }
>       }
>
> It all only compiles on all versions when I change the signature of the method m() to:
>
>           public static void m(List<? extends Supplier<? extends Map<String, 
> String>>> factories) {
>           }
>
> So unless there are more other cases of failing compiles, it seems to me like 
> something was actually fixed in the latest jdk21 build, and that the integration 
> of Sequenced Collections was just coincidence.
>
> Hope this helps.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/core-libs-dev/attachments/20230504/edcec4d3/attachment-0001.htm>


More information about the core-libs-dev mailing list