[ 
https://issues.apache.org/jira/browse/GROOVY-12037?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Paul King updated GROOVY-12037:
-------------------------------
    Description: 
h2. Problem

The GDK exposes only mutable conversions ({{toList}}/{{toSet}}) from 
{{Stream}}, {{BaseStream}}, {{Iterator}}, {{Iterable}} and arrays. Users 
wanting immutable results have to chain JDK helpers ({{List.copyOf}}, 
{{Collectors.toUnmodifiableSet}}) — or, since JDK 16, silently get one from 
{{stream.toList()}}: that call now resolves to the native {{Stream#toList()}} 
(unmodifiable) rather than the GDK extension (mutable {{ArrayList}}), with no 
compiler signal. Code that mutated the result and worked pre-JDK 16 can 
now throw {{UnsupportedOperationException}} after an unrelated JDK upgrade.

h2. Changes

* *Add {{toImmutableList}} / {{toImmutableSet}}* across every receiver that 
already has {{toList}}/{{toSet}}: {{BaseStream}}, {{Iterator}}, {{Iterable}}, 
{{T[]}}, and all 8 primitive array types. {{toImmutableSet}} also added for 
{{Stream}}. ({{toImmutableList(Stream)}} is omitted — native 
{{Stream#toList()}} already covers it.)
* *Add explicit-mutability {{toMutableList}} / {{toMutableSet}}* for {{Stream}} 
and {{BaseStream}}, so user intent stays unambiguous and the GDK is defensively 
positioned against any future JDK release adding more native instance methods 
that would shadow GDK extensions the way native {{Stream#toList()}} did.
* *Deprecate {{StreamGroovyMethods.toList(Stream)}}* — shadowed in both static 
and dynamic dispatch by native {{Stream#toList()}} on JDK 16+, so the 
method is effectively dead code in idiomatic call sites. Migration: 
{{toMutableList(Stream)}} for an explicit mutable list, native 
{{stream.toList()}} for an immutable one. The javadoc on the deprecated method 
is also updated to flag the JDK-16+ semantic shift for anyone who lands on it.

h2. Scope

|| File || New methods || Other ||
| {{StreamGroovyMethods}} | 7 ({{toMutableList(Stream)}}, 
{{toMutableList(BaseStream)}}, {{toMutableSet(Stream)}}, 
{{toMutableSet(BaseStream)}}, {{toImmutableList(BaseStream)}}, 
{{toImmutableSet(Stream)}}, {{toImmutableSet(BaseStream)}}) | +1 cached 
{{Collector}} ({{TO_UNMODIFIABLE_SET}}) used by the new immutable Set methods; 
1 deprecation; javadoc note |
| {{DefaultGroovyMethods}} | 4 ({{toImmutableList(Iterator)}}, 
{{toImmutableList(Iterable)}}, {{toImmutableSet(Iterator)}}, 
{{toImmutableSet(Iterable)}}) | — |
| {{ArrayGroovyMethods}} | 18 ({{toImmutableList}}/{{toImmutableSet}} × {{T[]}} 
+ 8 primitive arrays) | — |

29 new methods total, 1 deprecation. Fully additive: no public-API change to 
existing methods beyond the {{@Deprecated}} annotation and javadoc.

h2. Compatibility

* No source or binary breakage for any existing caller.
* The deprecation produces no warnings on internal Groovy code: only callers of 
{{StreamGroovyMethods.toList(Stream)}} are themselves {{@Deprecated}} bridge 
methods (in {{PluginDefaultGroovyMethods}}), and javac's {{-Xlint:deprecation}} 
does not flag {{@Deprecated → @Deprecated}} calls.
* Users on JDK 16+ writing idiomatic {{stream.toList()}} are already getting 
the native call — no change for them.
* The only callers who will see the deprecation warning are those explicitly 
invoking the GDK extension via the static-method form 
({{StreamGroovyMethods.toList(stream)}}), which is exactly the audience that 
needs to make an informed mutable-vs-immutable choice.

  was:
*Title:* GDK: add toMutableList/toImmutableList/toImmutableSet variants and 
deprecate toList(Stream) shim
*Type:* Improvement
*Component:* GDK
*Affects versions:* 6.0.0-SNAPSHOT
*Related:* GROOVY-A (cache Collectors in GDK stream and parallel extensions)

h2. Problem

The GDK exposes only mutable conversions ({{toList}}/{{toSet}}) from 
{{Stream}}, {{BaseStream}}, {{Iterator}}, {{Iterable}} and arrays. Users 
wanting immutable results have to chain JDK helpers ({{List.copyOf}}, 
{{Collectors.toUnmodifiableSet}}) — or, since JDK 16, silently get one from 
{{stream.toList()}}: that call now resolves to the native {{Stream#toList()}} 
(unmodifiable) rather than the GDK extension (mutable {{ArrayList}}), with no 
compiler signal. Code that mutated the result and worked pre-JDK 16 can 
now throw {{UnsupportedOperationException}} after an unrelated JDK upgrade.

h2. Changes

* *Add {{toImmutableList}} / {{toImmutableSet}}* across every receiver that 
already has {{toList}}/{{toSet}}: {{BaseStream}}, {{Iterator}}, {{Iterable}}, 
{{T[]}}, and all 8 primitive array types. {{toImmutableSet}} also added for 
{{Stream}}. ({{toImmutableList(Stream)}} is omitted — native 
{{Stream#toList()}} already covers it.)
* *Add explicit-mutability {{toMutableList}} / {{toMutableSet}}* for {{Stream}} 
and {{BaseStream}}, so user intent stays unambiguous and the GDK is defensively 
positioned against any future JDK release adding more native instance methods 
that would shadow GDK extensions the way native {{Stream#toList()}} did.
* *Deprecate {{StreamGroovyMethods.toList(Stream)}}* — shadowed in both static 
and dynamic dispatch by native {{Stream#toList()}} on JDK 16+, so the 
method is effectively dead code in idiomatic call sites. Migration: 
{{toMutableList(Stream)}} for an explicit mutable list, native 
{{stream.toList()}} for an immutable one. The javadoc on the deprecated method 
is also updated to flag the JDK-16+ semantic shift for anyone who lands on it.

h2. Scope

|| File || New methods || Other ||
| {{StreamGroovyMethods}} | 7 ({{toMutableList(Stream)}}, 
{{toMutableList(BaseStream)}}, {{toMutableSet(Stream)}}, 
{{toMutableSet(BaseStream)}}, {{toImmutableList(BaseStream)}}, 
{{toImmutableSet(Stream)}}, {{toImmutableSet(BaseStream)}}) | +1 cached 
{{Collector}} ({{TO_UNMODIFIABLE_SET}}) used by the new immutable Set methods; 
1 deprecation; javadoc note |
| {{DefaultGroovyMethods}} | 4 ({{toImmutableList(Iterator)}}, 
{{toImmutableList(Iterable)}}, {{toImmutableSet(Iterator)}}, 
{{toImmutableSet(Iterable)}}) | — |
| {{ArrayGroovyMethods}} | 18 ({{toImmutableList}}/{{toImmutableSet}} × {{T[]}} 
+ 8 primitive arrays) | — |

29 new methods total, 1 deprecation. Fully additive: no public-API change to 
existing methods beyond the {{@Deprecated}} annotation and javadoc.

h2. Compatibility

* No source or binary breakage for any existing caller.
* The deprecation produces no warnings on internal Groovy code: only callers of 
{{StreamGroovyMethods.toList(Stream)}} are themselves {{@Deprecated}} bridge 
methods (in {{PluginDefaultGroovyMethods}}), and javac's {{-Xlint:deprecation}} 
does not flag {{@Deprecated → @Deprecated}} calls.
* Users on JDK 16+ writing idiomatic {{stream.toList()}} are already getting 
the native call — no change for them.
* The only callers who will see the deprecation warning are those explicitly 
invoking the GDK extension via the static-method form 
({{StreamGroovyMethods.toList(stream)}}), which is exactly the audience that 
needs to make an informed mutable-vs-immutable choice.


> GDK: add toMutableList/toImmutableList/toImmutableSet variants and deprecate 
> toList(Stream) shim
> ------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12037
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12037
>             Project: Groovy
>          Issue Type: New Feature
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Major
>
> h2. Problem
> The GDK exposes only mutable conversions ({{toList}}/{{toSet}}) from 
> {{Stream}}, {{BaseStream}}, {{Iterator}}, {{Iterable}} and arrays. Users 
> wanting immutable results have to chain JDK helpers ({{List.copyOf}}, 
> {{Collectors.toUnmodifiableSet}}) — or, since JDK 16, silently get one from 
> {{stream.toList()}}: that call now resolves to the native {{Stream#toList()}} 
> (unmodifiable) rather than the GDK extension (mutable {{ArrayList}}), with no 
> compiler signal. Code that mutated the result and worked pre-JDK 16 can 
> now throw {{UnsupportedOperationException}} after an unrelated JDK upgrade.
> h2. Changes
> * *Add {{toImmutableList}} / {{toImmutableSet}}* across every receiver that 
> already has {{toList}}/{{toSet}}: {{BaseStream}}, {{Iterator}}, {{Iterable}}, 
> {{T[]}}, and all 8 primitive array types. {{toImmutableSet}} also added for 
> {{Stream}}. ({{toImmutableList(Stream)}} is omitted — native 
> {{Stream#toList()}} already covers it.)
> * *Add explicit-mutability {{toMutableList}} / {{toMutableSet}}* for 
> {{Stream}} and {{BaseStream}}, so user intent stays unambiguous and the GDK 
> is defensively positioned against any future JDK release adding more native 
> instance methods that would shadow GDK extensions the way native 
> {{Stream#toList()}} did.
> * *Deprecate {{StreamGroovyMethods.toList(Stream)}}* — shadowed in both 
> static and dynamic dispatch by native {{Stream#toList()}} on JDK 16+, so 
> the method is effectively dead code in idiomatic call sites. Migration: 
> {{toMutableList(Stream)}} for an explicit mutable list, native 
> {{stream.toList()}} for an immutable one. The javadoc on the deprecated 
> method is also updated to flag the JDK-16+ semantic shift for anyone who 
> lands on it.
> h2. Scope
> || File || New methods || Other ||
> | {{StreamGroovyMethods}} | 7 ({{toMutableList(Stream)}}, 
> {{toMutableList(BaseStream)}}, {{toMutableSet(Stream)}}, 
> {{toMutableSet(BaseStream)}}, {{toImmutableList(BaseStream)}}, 
> {{toImmutableSet(Stream)}}, {{toImmutableSet(BaseStream)}}) | +1 cached 
> {{Collector}} ({{TO_UNMODIFIABLE_SET}}) used by the new immutable Set 
> methods; 1 deprecation; javadoc note |
> | {{DefaultGroovyMethods}} | 4 ({{toImmutableList(Iterator)}}, 
> {{toImmutableList(Iterable)}}, {{toImmutableSet(Iterator)}}, 
> {{toImmutableSet(Iterable)}}) | — |
> | {{ArrayGroovyMethods}} | 18 ({{toImmutableList}}/{{toImmutableSet}} × 
> {{T[]}} + 8 primitive arrays) | — |
> 29 new methods total, 1 deprecation. Fully additive: no public-API change to 
> existing methods beyond the {{@Deprecated}} annotation and javadoc.
> h2. Compatibility
> * No source or binary breakage for any existing caller.
> * The deprecation produces no warnings on internal Groovy code: only callers 
> of {{StreamGroovyMethods.toList(Stream)}} are themselves {{@Deprecated}} 
> bridge methods (in {{PluginDefaultGroovyMethods}}), and javac's 
> {{-Xlint:deprecation}} does not flag {{@Deprecated → @Deprecated}} calls.
> * Users on JDK 16+ writing idiomatic {{stream.toList()}} are already getting 
> the native call — no change for them.
> * The only callers who will see the deprecation warning are those explicitly 
> invoking the GDK extension via the static-method form 
> ({{StreamGroovyMethods.toList(stream)}}), which is exactly the audience that 
> needs to make an informed mutable-vs-immutable choice.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to