Reputation: 33
I ran into a bad chunk of code today that equates to this:
[["asdf"]].each {String str -> println str}
println (["asdf"] as String)
Put this into groovy console (groovysh
) and you'll see this come out:
asdf
[asdf]
Can anyone explain why there's a difference in the outputs?
Upvotes: 3
Views: 121
Reputation: 171194
Groovy will unwrap a List if it means it will fit into the types you have defined in your closure.
If you don't define the type (or set it as List), it will behave as you expect:
// Both print '[a]'
[['a']].each { it -> println it }
[['a']].each { List it -> println it }
If however you define a type, it will attempt to unwrap the list and apply the contents to the defined parameters, so:
// Prints 'a'
[['a']].each { String it -> println it }
Think of it like varargs in java, or calling the closure with closure( *it )
in Groovy
It actually comes in pretty useful as you can do things like:
// Tokenize the strings into 2 element lists, then call each with these
// elements in separate variables
['a=b', 'b=c']*.tokenize( '=' )
.each { key, value ->
println "$key = $value"
}
Upvotes: 5
Reputation: 24498
The first output
I don't know for sure, but it seems as though Groovy will try to apply the closure to a single element of a list, iff there is only one.
Note that this:
[["asdf"]].each {String str -> println str }
is equivalent to this:
Closure c = { String s -> println s }
c(["asdf"])
and that these tests suggest the empirical evidence:
Closure c = { String s -> println s }
println "c test"
try { c(["asdf","def"]) } catch(Exception ex) { println "no" }
try { c(123) } catch(Exception ex) { println "no" }
try { c([]) } catch(Exception ex) { println "no" }
// this works:
try { c(["asdf"]) } catch(Exception ex) { println "no" }
Closure d = { Integer i -> println i }
println "d test"
try { d([22,33]) } catch(Exception ex) { println "no" }
try { d("abc") } catch(Exception ex) { println "no" }
try { d([]) } catch(Exception ex) { println "no" }
// this works:
try { d([22]) } catch(Exception ex) { println "no" }
The second output
Simply note that this:
println (["asdf"] as String)
is likely equivalent to this:
List<String> list = ["asdf"]
println list.toString()
Upvotes: 1