A quirk in Groovy maps and GString coercion
I slipped into my old Java ways recently and lost a bit of Groovy magic in the process. I was calling the get method on a map and getting null back, though I was quite confident I should have gotten a value from the map.
Let’s start with a simple map in Groovy:
def map = [a:'x',b:'y',c:'z']
Now we can print out the value for key ‘a’ with magical Groovy syntax:
println map['a'] // this outputs x
Or we can slip back into our old Java ways and it will still works.
println map.get(‘a’) // this also outputs x
But what if we want to look up a value using a GString with an inner variable instead of the normal String?
def aVar = ‘a’
println map["$aVar"] // still good, this outputs x
println map.get(“$aVar”) // oh snap! this outputs null!
As you can see, using the Java style of .get() using a GString that contains a variable doesn’t work.
But… wait a second! Aren’t ‘a’ and “$aVar” supposed to be the same thing in Groovy!?
println ‘a’ == “$aVar” // true
Not entirely. Yes, you learned that == in Groovy is the same as calling .equals in Java. But check this out!
println ‘a’.equals(“$aVar”) // double snap!! it’s false!!
So == obviously isn’t exactly the same as .equals – if you check out the Groovy Operator Overloading page, you’ll see a little sidenote:
** Note: The == operator doesn’t always exactly match the .equals() method. You can think of them as equivalent in most situations. In situations where two objects might be thought “equal” via normal Groovy “coercion” mechanisms, the == operator will report them as equal; the .equals() method will not do so if doing so would break the normal rules Java has around the equals method. Expect further improvements to Groovy over time to provide clearer, more powerful and more consistent behavior in this area.
In case you’ve read that sidenote before but you didn’t know what it meant by “coercion” mechanisms, now you know.
Keep all this in mind next time you’re dealing with a map in Groovy. Stick to using the Groovy array-ish square brackets instead of using the traditional .get() and you’ll avoid a potential headache.