What is a Groovy GString, how does interpolation work in build.gradle, and what pitfalls should you watch for?
answer
- single quote = String, double = GString
- ${expr} / $prop interpolation
- GString is CharSequence, not String
- .toString() for strict String use
- ${ -> ... } defers evaluation
basics
~10 sA GString is a double-quoted Groovy string that supports ${...} interpolation, e.g. "guava:${libVersion}". Single-quoted strings are plain Strings with no interpolation. Watch out: a GString isn't a java.lang.String and may be evaluated lazily.
solid answer
~50 sGroovy has two string literals. **Single quotes** (`'...'`) are plain `java.lang.String` — no interpolation. **Double quotes** (`"..."`) are `GString` (GStringImpl) and support `${expr}` and `$prop` interpolation. So `"com.google.guava:guava:${libVersion}"` substitutes the variable. Key pitfalls: (1) a `GString` is **not** a `String` — most Gradle APIs coerce it, but code or maps doing identity/`instanceof String` checks or using it as a strict `String` key can misbehave; call `.toString()` when you need a real String. (2) Interpolation is **lazy in spirit** — the embedded expression is evaluated when the GString is rendered, so a closure-based placeholder (`${ -> computeLater() }`) defers evaluation, useful for values not yet set. (3) Prefer single quotes for fixed coordinates/paths (clearer intent, no accidental `$`), and reserve double quotes for when you actually interpolate. (4) `$` inside a double-quoted string must be escaped (`\$`) if literal.
code
groovy · 9 linesdef libVersion = '33.0.0-jre'
dependencies {
implementation "com.google.guava:guava:${libVersion}" // GString, interpolated
implementation 'org.slf4j:slf4j-api:2.0.13' // plain String, no $
}
// deferred: evaluated when rendered, not now
version = "${ -> project.findProperty('appVersion') ?: 'SNAPSHOT' }"go deeper
Know double quotes + ${var} interpolate and single quotes don't.
Explain GString vs String, .toString() coercion, and the single-vs-double-quote convention.
Discuss the not-a-String map-key pitfall and ${ -> ... } deferred evaluation for configuration ordering.
Standardize quoting/interpolation conventions (or move to Kotlin DSL/version catalogs) to remove a whole class of subtle string bugs across the build.
## Two string types Groovy distinguishes string literals by quote: ```groovy 'plain' // java.lang.String, no interpolation "hello ${name}" // groovy.lang.GString, interpolated ``` In a `GString`, `${expression}` (or the short `$identifier` / `$obj.prop`) embeds a value that is computed and inserted when the string is rendered to text. This is what makes: ```groovy def libVersion = '33.0.0-jre' implementation "com.google.guava:guava:${libVersion}" ``` produce `com.google.guava:guava:33.0.0-jre`. ## GString is not String A `GString` holds the literal text segments plus the unevaluated value references; it implements `CharSequence`, not `String`. Most Gradle DSL methods accept `Object`/`CharSequence` and coerce it, so you rarely notice. But problems arise when: - You put a GString into a `Map` and later look it up with a plain `String` key (their `hashCode`/`equals` differ) — use `.toString()`. - An API strictly requires `String` or does `instanceof String`. Rule: when handing an interpolated value to something that genuinely needs `java.lang.String`, call `"...".toString()`. ## Lazy / deferred interpolation The embedded expression is evaluated at **render** time, not at literal-creation time — but in practice the value is captured when the GString is built unless you embed a **closure**: ```groovy version = "${ -> project.findProperty('appVersion') ?: 'SNAPSHOT' }" ``` Using `${ -> ... }` defers evaluation until the string is actually converted to text, which matters when the underlying value is set later in the configuration phase. A plain `${expr}` captures the result of evaluating `expr` at that point. ## Escaping and style - A literal `$` in a double-quoted string must be escaped: `"price is \$5"`. - Prefer **single quotes** for constant coordinates and paths — it signals 'no interpolation here' and avoids accidental `$` interpretation. - Use double quotes only where you interpolate; this is a common readability convention in build scripts. ## Why it matters Understanding GStrings explains odd map-lookup failures, why some teams insist on single quotes for dependency strings, and how to defer a value with `${ -> ... }` when configuration order matters. It also clarifies why Kotlin DSL uses `$var`/`${expr}` but always with real `String`s, removing the not-a-String surprise.
- Why might using a GString as a Map key cause a lookup with a String key to fail?A GString is a separate CharSequence type whose equals/hashCode differ from java.lang.String, so the keys don't match. Call .toString() to normalize before using it as a key.
- How do you defer evaluation of an interpolated value until the string is rendered?Embed a closure: "${ -> someValue }". The closure runs at render time, so a value set later in the configuration phase is picked up.
saying these in an interview costs you the question
- Claiming single-quoted strings interpolate ${...}.
- Assuming a GString is always interchangeable with java.lang.String (it can break map keys / strict String APIs).