[External] : The introduction of Sequenced collections is not a source compatible change
Alan Snyder
javalists at cbfiddle.com
Fri May 5 01:26:27 UTC 2023
This is an interesting issue, and it raises the question of how such issues are resolved.
My impression is that historically source code compatibility was given a high weight.
Is that not the case now?
Although the example may be carefully constructed, my understanding is that the problem
surfaced with programs that were not intentionally constructed to show this problem.
This issue is more general than the examples in your compatibility analysis report,
which I believe are limited to fixed sets of class names and method names.
Although you say that this issue does not represent a “bug” in type inference, I can’t help but
wonder whether a more flexible type system could handle this change without breaking
source compatibility.
If so, then a case could be made to delay the introduction of Sequenced Collections until the
type system can handle it without source incompatibility. Java has survived for many years
without Sequenced Collections. I don’t see a need to rush them out the door.
Being able to compatibly add new common supertypes would be valuable.
There are many other reasons for improving the type system. Of particular interest to me is
the mismatch between the current subtyping rules for parameterized types and the needs
of immutable collection types. (I understand that JDK did not go down this path, but
others have and will.)
> On May 4, 2023, at 3:45 PM, Stuart Marks <stuart.marks at oracle.com> wrote:
>
> 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/72e522d0/attachment-0001.htm>
More information about the core-libs-dev
mailing list