def A = "A"
def B = "B"
def map = ["${A}":1, (B):2, C:3]
println map
println map[A]
println map[B]
println map["C"]
println map[C]
What will be output?
The first thing to consider is, what will groovy think the map is, given the 3 different ways entry are created. However, printing it will return:
[A:1, B:2, C:3]
Looks like it all ended up being the same thing, no?
Now consider what attempting to print a, b and c will give you:
null
2
3
Exception thrown
groovy.lang.MissingPropertyException: No such property: C for class: ConsoleScript27
at ConsoleScript27.run(ConsoleScript27:10)
Now, let's think about what we've done here. With A, we've added it using the normal syntax for dereferencing an object inside of a string, by surround it with ${} inside the string itself, somewhat similar to JSTL. This is returning the string 'A' correctly, but when attempting to reference it, it's not found, thus returning null.
With B, the correct way to dereference and object was used, by surrounding it with a parens. This works as expected, and the referring object can even be used as a key.
C used the string property approach. That is, groovy treated it as the literal string 'C'. This is why referencing it as a ["C"] worked and [C] caused an exception.
The following groovy page contains all you need to know about map manipulation in groovy: http://groovy.codehaus.org/JN1035-Maps
Next, let's consider some of the very useful operations that groovy adds to normal java collections. In this case, max(). What will the following return?
def someList = ["75", 32, 3.2]
println someList.max {item ->
return null
}
The answer is "75". This is because it's the first item in the list. If the closure passed to the max method returns null, the first item in the collection will be returned.
Now, imagine the following scenario. You mess up how you reference the map, inadvertently causing null to be returned for all calls. You then use this map in a closure for the max method. You don't catch any of this because in your unit test the first element should be the max. Now, imagine the list you're operating on was obtained from a database via GORM, which means the order will vary from call to call, since there is no order guarantee without an orderby clause, which isn't there. So, on the webpage you'll see the value change everytime you call it, but when you debug, the map will look correct. Hours later, you'll discover that you didn't create the Map correctly, even though inspecting it in debug, and printing it out in unit testing will show that it looks correct.
While I don't know Groovy that well yet, I can understand at least some of the mechanisms that would lead to that behavior. However, I wonder two things:
- Why does the map print out the way it does? I need to do some more digging into the Groovy Map implementation to figure out what underlying mechanism is causing it, but it seems like there could be some kind of way of determining how it's being stored. Meaning, there's obvious differences between the 3 entries, but you won't see it visually when printing, and you won't be able to determine it easily in a debugger. (Although, I may have missed something important when looking)
- Should the closure returning null for every element behave this way? I suppose there isn't much way around it, when you consider a scenario where null is returned in some but not all cases.