Groovy and Java Strings

I am completely new to Groovy, and while using it a bit for our CI (Jenkins), I stumbled upon the non-intuitive difference between a Groovy and a Java String.

Some code

I just added some items to a Set and tried to look for them:

Set<String> s = new HashSet<String>();
def artifactId = "commons-lang3"
def version = "3.17.0"
def jarName = "${artifactId}-${version}.jar"
s.add(jarName)
println(s.contains("commons-lang3-3.17.0.jar")) // false

I initially thought it was a whitespace issue, so I tried to trim() the string… and it worked! But there was no whitespace.

Set<String> s = new HashSet<String>();
def artifactId = "commons-lang3"
def version = "3.17.0"
def jarName = "${artifactId}-${version}.jar"
s.add(jarName.trim())
println(s.contains("commons-lang3-3.17.0.jar")) // true

Not the same String

A Groovy String is not a Java string, that’s all…:

def jarName1 = "${artifactId}-${version}.jar"
def jarName2 = artifactId + "-" + version + ".jar"
println(jarName2.equals(jarName1)) // false
println(jarName1.equals(jarName2.trim())) // false
println(jarName2.equals(jarName1.trim())) // true
println(jarName1.getClass().getSimpleName()) // GStringImpl
println(jarName2.getClass().getSimpleName()) // String
println(jarName1.toString()) // String

So, depending on the type of string used in a Set or a Map, it may or may not find them depending on which version is used.
What I find worse is that calling trim() on a GString actually returns a Java String and not a GString.

Conclusion

Read the documentation…
Despite the behavior being documented on the Groovy website, I find it completely unintuitive and very prone to subtle bugs…
I dislike Groovy.