[
https://issues.apache.org/jira/browse/GROOVY-12644?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Paul King resolved GROOVY-12644.
--------------------------------
Fix Version/s: 7.0.0-alpha-1
Assignee: Paul King
Resolution: Fixed
> make method return follow similar rules to assignment for literals
> ------------------------------------------------------------------
>
> Key: GROOVY-12644
> URL: https://issues.apache.org/jira/browse/GROOVY-12644
> Project: Groovy
> Issue Type: Improvement
> Reporter: Paul King
> Assignee: Paul King
> Priority: Major
> Fix For: 7.0.0-alpha-1
>
>
> h3. Problem
> Under {{@TypeChecked}} and {{@CompileStatic}}, a list or map literal
> *returned* from a method is accepted only when the literal's own class
> ({{ArrayList}} or {{LinkedHashMap}}) is assignable to the declared return
> type. The rules that apply when the same literal is assigned to a variable of
> that type (the "list and map constructors" section of the type checking
> documentation, and GROOVY-12643) do not apply to return values. Dynamic code
> converts the returned value to the return type, as it does for an assignment.
> Results on 6.0.0 (the same on 3.0.25 and 4.0.33, with or without an explicit
> {{return}}):
> ||Method||Dynamic||{{@TypeChecked}} / {{@CompileStatic}}||
> |{{List m() \{ [3, 1] \}}}|{{ArrayList [3, 1]}}|{{ArrayList [3, 1]}}|
> |{{HashMap m() \{ [a: 1] \}}}|{{LinkedHashMap [a:1]}}|{{LinkedHashMap [a:1]}}|
> |{{Set m() \{ [3, 1, 3] \}}}|{{LinkedHashSet [3, 1]}}|Cannot return value of
> type java.util.ArrayList<java.lang.Integer> for method returning
> java.util.Set|
> |{{Set m(boolean b) \{ b ? [3, 1] : [2] \}}}|{{LinkedHashSet [3, 1]}}|same
> error|
> |{{Set m(boolean b) \{ if (b) return [3, 1]; [2] \}}}|{{LinkedHashSet [3,
> 1]}}|same error|
> |{{LinkedHashSet m() \{ [3, 1] \}}}|{{LinkedHashSet [3, 1]}}|same error|
> |{{TreeMap m() \{ [b: 2, a: 1] \}}}|{{TreeMap [a:1, b:2]}}|Cannot return
> value of type java.util.LinkedHashMap<...> for method returning
> java.util.TreeMap|
> The same literals compile when assigned instead:
> {code:groovy}
> @CompileStatic
> Set m() {
> Set s = [3, 1, 3] // compiles: LinkedHashSet
> s
> }
> {code}
> So the common workaround is a local variable, or an explicit {{as Set}}.
> With GROOVY-12643, more declaration targets are accepted ({{SortedSet}},
> {{Deque}}, {{SortedMap}}, {{LinkedList}}, ...), which widens the gap between
> what a declaration and a return accept.
> h3. Closures and lambdas are different
> A closure or lambda with a declared SAM return type rejects the same literals:
> ||Expression||Dynamic||{{@TypeChecked}} / {{@CompileStatic}}||
> |{{Supplier<Set<Integer>> s = () -> [3, 1, 3]}}|{{s.get()}} returns
> {{ArrayList [3, 1, 3]}}|Cannot return value of type java.util.ArrayList<...>|
> |{{Supplier<Set<Integer>> s = \{ -> [3, 1, 3] \}}}|{{s.get()}} returns
> {{ArrayList [3, 1, 3]}}|same error|
> |{{Closure<Set<Integer>> c = \{ -> [3, 1, 3] \}}}|{{c()}} returns {{ArrayList
> [3, 1, 3]}}|same error|
> |{{Supplier<TreeMap<String, Integer>> s = () -> [b: 2, a: 1]}}|{{s.get()}}
> returns {{LinkedHashMap}}|same error|
> Here dynamic code does *not* convert the return value, so a
> {{Supplier<Set<Integer>>}} hands back an {{ArrayList}}. The static rejection
> protects against a real mismatch, and accepting these literals would need the
> closure or lambda to convert its result. So this issue proposes no change for
> them.
> h3. Question for feedback
> Should a literal returned from a method follow the same rules as a literal
> assigned to a variable of the method's return type?
> If yes, the proposal is to check a returned list or map literal as an
> assignment to the declared return type is checked: the {{Set}} exemption, the
> constructor shorthand, and the GROOVY-12643 interface defaults and copy rule.
> Under {{@CompileStatic}}, a value that is not an instance of the return type
> should then reach the return type through the existing cast, which calls
> {{DefaultTypeTransformation.castToType}}, as an assignment does. This is not
> yet verified, and needs a test for each kind of target.
> If no, the type checking documentation should say that the list and map
> constructor rules apply to declarations and assignments only.
> h3. Out of scope
> * Closure and lambda return values (see above).
> * Method arguments: a list or map argument is not converted to the parameter
> type, in any mode, by design.
> * Non-literal return values, such as {{Set m() \{ aList \}}}, stay an error
> under static checking, as in Java.
> h3. Related
> * GROOVY-12643: the declaration rules for list and map literals assigned to
> collection and map types.
> * GROOVY-12585: the runtime default implementations used by the cast.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)