[
https://issues.apache.org/jira/browse/GROOVY-12536?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Daniel Sun reassigned GROOVY-12536:
-----------------------------------
Assignee: Daniel Sun
> SC: a lambda or method reference taking a String throws ClassCastException
> for a GString element of a List<String>
> ------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12536
> URL: https://issues.apache.org/jira/browse/GROOVY-12536
> Project: Groovy
> Issue Type: Bug
> Reporter: Paul King
> Assignee: Daniel Sun
> Priority: Major
>
> A {{List<String>}} filled by dynamic code may hold {{GString}} elements,
> because generic types are not checked at runtime. In statically compiled
> code, Groovy converts such an element to a {{String}} wherever a {{String}}
> is required. That includes assignments, arguments, calls to {{String}}-only
> methods, and closure parameters ({{list.collect { it.toUpperCase() }}}).
> Native lambdas and method references are the exception: they throw a
> {{ClassCastException}}, even when the body only uses {{CharSequence}} or
> {{Object}} members:
> {code:groovy}
> import groovy.transform.CompileStatic
> @CompileStatic
> class L {
> static a(List<String> l) { l.stream().map(s ->
> s.toUpperCase()).findFirst().get() }
> static b(List<String> l) {
> l.stream().map(String::toUpperCase).findFirst().get() }
> static c(List<String> l) { l.stream().map(s ->
> s.length()).findFirst().get() }
> static d(List<String> l) { l.stream().map(s ->
> s.toString()).findFirst().get() }
> static e(List<String> l) { l.stream().map { it.toUpperCase()
> }.findFirst().get() }
> }
> def n = 'ab'
> List<String> gs = ["$n"] // element is a GString
> {code}
> ||Call||@CompileStatic (4.0.29, 5.0.5, 6.0.0, master)||Dynamic||
> |{{a}}: {{s -> s.toUpperCase()}}|ClassCastException|{{AB}}|
> |{{b}}: {{String::toUpperCase}}|ClassCastException|MissingMethodException|
> |{{c}}: {{s -> s.length()}}|ClassCastException|{{2}}|
> |{{d}}: {{s -> s.toString()}}|ClassCastException|{{ab}}|
> |{{e}}: closure {{{ it.toUpperCase() }}}|{{AB}} on master (CCE before
> GROOVY-12469 follow-up)|{{AB}}|
> The lambda's parameter is compiled as {{String}}, so the cast happens in the
> bridge that {{LambdaMetafactory}} generates for {{Function.apply(Object)}},
> before any generated code can convert the value. A possible fix is to give
> the synthetic lambda method an erased parameter type when the parameter type
> comes from generics, then convert a {{GString}} to {{String}} inside the
> lambda, as is now done for closure parameters. The method reference case
> would need the same treatment through a synthetic lambda.
> Related observation: in dynamic code, {{String::toUpperCase}} applied to a
> {{GString}} throws {{MissingMethodException}}, while {{s -> s.toUpperCase()}}
> works. That may be worth handling separately.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)