Luke E
Luke E

Reputation: 33

Groovy cast behaves inconsistently

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

Answers (2)

tim_yates
tim_yates

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

Michael Easter
Michael Easter

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

Related Questions