Some weeks back my pair and I were fixing a bug that finally boiled down to the fact that the contains() method on a List returned false if the list contained the GString counterpart of the same literal.
Having played a bit I learned a couple of things about GStrings. Please find some bullet points and the code snippet bellow.
Type:
1. def var1 = "this is a String instead of a GString even if it is surrounded by double quotes - can has no dollar sign"
2. def var2 = "this is a GString because it contains a dollar sign - $var1"
3. String var3 = "this is a string because it was defined as a String"
4. String var4 = "$var3 - this is still a string because it was defined as a String"
Equality
1. a String and a GString can be ==
2. a String and a GString are NEVER equals()
3. as a consequence a list containing a literal will not return true if the type (GString/String) does not match
class WhereIsYourDummyTests extends GroovyTestCase {
def e = "y"
def dummy = "dumm$e"
void assertTypeIsStringForAFixGString() {
assert e instanceof java.lang.String
assert dummy instanceof groovy.lang.GString
}
void testEqualsNotEquals() {
assert "dummy" == dummy
assert dummy == "dummy"
// WATCH OUT HERE
assertFalse("dummy".equals(dummy))
assertFalse(dummy.equals("dummy"))
}
void testContainsDoesntContain() {
assert ['dummy'].contains("dummy")
assert ["dummy"].contains('dummy')
assert [dummy].contains(dummy)
// THIS IS TRICKY:
assert ![dummy].contains("dummy")
assert ![dummy].contains('dummy')
assert !["dummy"].contains(dummy)
assert !['dummy'].contains(dummy)
assert [dummy.toString()].contains("dummy")
assert [dummy.toString()].contains('dummy')
}
void testContainsIfVariablesAreStringInsteadOfDef() {
String e = "y" // String instead of def
String dummy = "dumm$e" // String instead of def
assert ['dummy'].contains("dummy")
assert ["dummy"].contains('dummy')
assert [dummy].contains(dummy)
assert [dummy].contains("dummy")
assert [dummy].contains('dummy')
assert ["dummy"].contains(dummy)
assert ['dummy'].contains(dummy)
assert [dummy.toString()].contains("dummy")
assert [dummy.toString()].contains('dummy')
}
}
2 comments:
GString's equals implementation actually violates the contract of equals which states that x.equals(y) must return the same value as y.equals(x).
The Groovy == operator 'cheats' its way round this by actually using "x.compareTo(y) == 0" if the objects on either side of the operator implement java.util.Comparable which both String and GString do.
You also see this problem when comparing maps, e.g.
String first = 'Fred'
String last = 'Bloggs'
def data = [fullName: "${first} ${last}]
assertEquals([fullName: 'Fred Bloggs', data])
The workarounds are
1. Test each element of your map explicitly
2. Use "${first} ${last}".toString() in your code
3. Build strings as you would in java using +, concat, sprintf etc
All Yuck
Post a Comment