I am trying to predict the behavior of this code in Groovy
userList.find { }
.find documentation
When the find method is invoked and passed a closure it returns the first element that evaluates the closure into Groovies understanding of true.
When the find method is called without any parameters it returns the first object in the list that matches true according to Groovy truth.
What happens if an empty closure is used?
Will it evaluate to true and thus the first element of the list is returned?
Will it always evaluate to false and after iterating over the list null is returned?
Will it be behave like .find()?
From the Groovy Closures Formal Definition (Alternative Source):
Closures always have a return value. The value may be specified via one or more explicit return statement in the closure body, or as the value of the last executed statement if return is not explicitly specified. If the last executed statement has no value (for example, if the last statement is a call to a void method), then null is returned.
And from Groovy Truth
Object references
Non-null object references are coerced to true.
...
assert !null
That suggests to me that the truth of the return value of an empty closure is always false, so find will never find anything, and thus presumably return null.
there is no definition of "empty closure" only if you delegate this closure to a Map, then the Map keeps empty :
Map closureToMap(Closure c, Map attrs=[:]) {
c.resolveStrategy = Closure.DELEGATE_FIRST
c.delegate = attrs
c()
return attrs
}
def myEmptyClosure = {}
assert closureToMap(myEmptyClosure).isEmpty()
Related
Suppose the every or any method is being evaluated on a long list, and the value of the closure on the first element is enough to determine the final result (true for any, false for every). Do these methods continue processing the rest of the elements of the list or do they immediately return?
def lst = [1,2,3,4]
//Do either of these functions evaluate the 2nd, 3rd and 4th element of lst?
boolean value1 = lst.any{element -> element == 1}
boolean value2 = lst.every {element -> element == 2}
any() and every() short-circuit and return as soon as possible.
You can see this in the source for any() and every(). These methods each iterate over each object evaluating the closure and return if the condition is (for any())/is not (for every()) met.
Is there a way to instantiate the value of map lazy?
For example
class MapTest {
#Lazy(soft = true) HashMap<String, List<String>> map
}
Doing like this I can use this call and get null without recieving NullPointerException
new MapTest().map.key1
However attempt to call
map.key1.remove(1)
will lead to NullPointerException due the value being null. (it would be fine if it threw IndexOutOfBounds exception)
Is there a way to instantiate the list value of the map?
try map.withDefault :
def map = [:].withDefault { [] }
assert map.key1.isEmpty()
Some explanation :
[:] is the groovy way to instantiate an empty hash map
withDefault is a groovy method on a map wich take a closure. this closure is call every time a key is requested to initialize the value if it doesn't exist. this closure take one parameter (the key) and should the value
[] is the groovy way to create an empty list - { [] } is a closure wich return an empty list for every key
see others examples here
While studying the Groovy (2.4.4) syntax in the official documentation, I came across the special behavior concerning maps with GStrings as identifiers. As described in the documentation, GStrings are a bad idea as (hash)map identifiers, because the hashcodes of a non-evaluated GString-object differs from a regular String-object with the same representation as the evaluated GString.
Example:
def key = "id"
def m = ["${key}": "value for ${key}"]
println "id".hashCode() // prints "3355"
println "${key}".hashCode() // prints "3392", different hashcode
assert m["id"] == null // evaluates true
However, my intuitive expectation was that using the actual GString identifier to address a key in the map will in fact deliver the value - but it does not.
def key = "id"
def m = ["${key}": "value for ${key}"]
assert m["${key}"] == null // evaluates also true, not expected
That made me curious. So I had several suggestions concerning this issue and did some experiments.
(pls keep in my mind that I am new to Groovy and I was just brainstorming on the fly - continue to Suggestion #4 if you do not want to read how I tried to examine the cause of the issue)
Suggestion #1. hashcode for GString objects works/is implemented somewhat non-deterministic for whatever reason and delivers different results depending on the context or the actual object.
That turned out to be nonsense quite fast:
println "${key}".hashCode() // prints "3392"
// do sth else
println "${key}".hashCode() // still "3392"
Suggestion #2. The actual key in the map or the map item does not have the expected representation or hashcode.
I took a closer look at the item in the map, the key, and its hashcode.
println m // prints "[id:value for id]", as expected
m.each {
it -> println key.hashCode()
} // prints "3355" - hashcode of the String "id"
So the hashcode of the key inside the map is different from the GString hashcode. HA! or not. Though it is nice to know, it is actually not relevant because I still do know the actual hashcodes in the map index. I just rehashed a key that has been transformed to a string after being put into the index. So what else?
Suggestion #3. The equals-method of a GString has an unknown or non- implemented behavior.
No matter whether two hashcodes are equal, they may not represent the same object in a map. That depends on the implementation of the equals method for the class of the key-object. If the equals-method is, for instance, not implemented, two objects are not equal even if the hashcode is identical and therefore the desired map key cannot be adressed properly. So I tried:
def a = "${key}"
def b = "${key}"
assert a.equals(b) // returns true (unfortunate but expected)
So two representations of the same GString are equal by default.
I skip some others ideas I tried and continue with the last thing I tried just before I was going to write this post.
Suggestion #4. The syntax of access matters.
That was a real killer of understanding. I knew before: There are syntactically different ways two access map values. Each way has its restrictions, but I thought the results stay the same. Well, this came up:
def key = "id"
def m = ["${key}": "value for ${key}"]
assert m["id"] == null // as before
assert m["${key}"] == null // as before
assert m.get("${key}") == null // assertion fails, value returned
So if I use the get-method of a map, I get the actual value in the way I expected it to in the first place.
What is the explanation for this map access behavior concerning GStrings? (or what kind of rookie mistake is hidden here?)
Thanks for your patience.
EDIT: I am afraid that my actual question is not clearly stated, so here is the case in short and concise:
When I have a map with a GString as a key like this
def m = ["${key}": "value for ${key}"]
why does this return the value
println m.get("${key}")
but that does not
println m["${key}"]
?
You can look at this matter with a very different approach. A map is supposed to have immutable keys (at least for hashcode and equals), because the map implementation depends on this. GString is mutable, thus not really suited for map keys in general. There is also the problem of calling String#equals(GString). GString is a Groovy class, so we can influence the equals method to equal to a String just fine. But String is very different. That means calling equals on a String with a GString will always be false in the Java world, even if hashcode() would behave the same for String and GString. And now imagine a map with String keys and you ask the map for a value with a GString. It would always return null. On the other hand a map with GString keys queried with a String could return the "proper" value. This means there will always be a disconnection.
And because of this problem GString#hashCode() is not equal to String#hashCode() on purpose.
It is in no way non-deterministic, but a GString hashcode can change, if the participating objects change their toString representation:
def map = [:]
def gstring = "$map"
def hashCodeOld = gstring.hashCode()
assert hashCodeOld == gstring.hashCode()
map.foo = "bar"
assert hashCodeOld != gstring.hashCode()
Here the toString representation of map will change for Groovy and GString, thus the GString will produce a different hashcode
According to the Groovy docs, the == is just a "clever" equals() as it also takes care of avoiding NullPointerException:
Java’s == is actually Groovy’s is() method, and Groovy’s == is a clever equals()!
[...]
But to do the usual equals() comparison, you should prefer Groovy’s ==, as it also takes care of avoiding NullPointerException, independently of whether the left or right is null or not.
So, the == and equals() should return the same value if the objects are not null. However, I'm getting unexpected results on executing the following script:
println "${'test'}" == 'test'
println "${'test'}".equals('test')
The output that I'm getting is:
true
false
Is this a known bug related to GStringImpl or something that I'm missing?
Nice question, the surprising thing about the code above is that
println "${'test'}".equals('test')
returns false. The other line of code returns the expected result, so let's forget about that.
Summary
"${'test'}".equals('test')
The object that equals is called on is of type GStringImpl whereas 'test' is of type String, so they are not considered equal.
But Why?
Obviously the GStringImpl implementation of equals could have been written such that when it is passed a String that contain the same characters as this, it returns true. Prima facie, this seems like a reasonable thing to do.
I'm guessing that the reason it wasn't written this way is because it would violate the equals contract, which states that:
It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
The implementation of String.equals(Object other) will always return false when passed a GSStringImpl, so if GStringImpl.equals(Object other) returns true when passed any String, it would be in violation of the symmetric requirement.
In groovy a == b checks first for a compareTo method and uses a.compareTo(b) == 0 if a compareTo method exists. Otherwise it will use equals.
Since Strings and GStrings implement Comparable there is a compareTo method available.
The following prints true, as expected:
println "${'test'}".compareTo('test') == 0
The behaviour of == is documented in the Groovy Language Documentation:
In Java == means equality of primitive types or identity for objects. In Groovy == means equality in all cases. It translates to a.compareTo(b) == 0, when evaluating equality for Comparable objects, and a.equals(b) otherwise. To check for identity (reference equality), use the is method: a.is(b). From Groovy 3, you can also use the === operator (or negated version): a === b (or c !== d).
The full list of operators are provided in the Groovy Language Documentation for operator overloading:
Operator
Method
+
a.plus(b)
-
a.minus(b)
*
a.multiply(b)
/
a.div(b)
%
a.mod(b)
**
a.power(b)
|
a.or(b)
&
a.and(b)
^
a.xor(b)
as
a.asType(b)
a()
a.call()
a[b]
a.getAt(b)
a[b] = c
a.putAt(b, c)
a in b
b.isCase(a)
<<
a.leftShift(b)
>>
a.rightShift(b)
>>>
a.rightShiftUnsigned(b)
++
a.next()
--
a.previous()
+a
a.positive()
-a
a.negative()
~a
a.bitwiseNegate()
Leaving this here as an additional answer, so it can be found easily for Groovy beginners.
I am explicitly transforming the GString to a normal String before comparing it.
println "${'test'}".equals("test");
println "${'test'}".toString().equals("test");
results in
false
true
The Groovy "in" operator seems to mean different things in different cases. Sometimes x in y means y.contains(x) and sometimes it seems to call y.isCase(x).
How does Groovy know which one to call? Is there a particular class or set of classes that Groovy knows about which use the .contains method? Or is the behavior triggered by the existence of a method on one of the objects? Are there any cases where the in operator gets changed into something else entirely?
I did some experimentation and it looks like the in operator is based on the isCase method only as demonstrated by the following code
class MyList extends ArrayList {
boolean isCase(Object val) {
return val == 66
}
}
def myList = new MyList()
myList << 55
55 in myList // Returns false but myList.contains(55) returns true
66 in myList // Returns true but myList.contains(66) returns false
For the JDK collection classes I guess it just seems like the in operator is based on contains() because isCase() calls contains() for those classes.
It's actually all based on isCase. Groovy adds an isCase method to Collections that is based on the contains method. Any class with isCase can be used with in.
in is the "Membership operator".
From the documentation for Groovy 3 (emphasis mine):
8.6. Membership operator
The membership operator (in) is equivalent to calling the isCase
method. In the context of a List, it is equivalent to calling
contains, like in the following example:
def list = ['Grace','Rob','Emmy']
assert ('Emmy' in list) # (1)
(1) equivalent to calling list.contains('Emmy') or list.isCase('Emmy')
So, Groovy always calls isCase, which in case of a List maps to contains.