Groovy from the ground up

Numbers and text together

Quoting that interpolates and quoting that does not, braced expressions inside text, and the arithmetic operators that decide whether you get 2 or 2.5.

Part 3 of 12

Beginner15 min readBack to Courses

Two kinds of quotes

A double-quoted string in Groovy is a GString: it looks inside itself for $name and replaces the name with the value. So "Hello, $name!" greets whoever name holds. A single-quoted string is plain text and looks at nothing: 'Hello, $name!' prints the dollar sign and the word.

That difference is the whole quoting story for now, and it is worth being deliberate about: single quotes when the text is fixed, double quotes when a value goes in. Groovy does not care which you prefer, but a file where both are used at random is a file that hides its interpolations.

Braces are for expressions

$name only works when the thing after the dollar sign is a name. Arithmetic needs braces: "${score * 2}" runs the expression and pastes the answer into the text. The braces also stop the name where you want it stopped — "$namebook" looks for a variable called namebook, while "${name}book" appends the word to the value. Without them, "$names" asks for a variable called names, and the failure is a missing-property error naming a variable you never wrote.

Anything can go inside the braces: a method call, a property, a comparison. The result is converted to text with the same rules println uses, so a list becomes [a, b] and a number keeps its usual shape.

The arithmetic operators

+ - * / are the familiar four, % is the remainder and ** is a power. The one that catches people out is /: it always answers the exact quotient, so 10 / 4 is 2.5 even though both sides are whole numbers. 10.intdiv(4) is the whole-number division, and 10 % 4 is what is left over. ** binds tighter than *, which binds tighter than +, so 2 + 3 ** 2 is 11 rather than 25.

Because + also joins text, the order of a mixed expression decides the answer. "Total: " + (3 + 4) is Total: 7, and "Total: " + 3 + 4 is Total: 34 — the brackets are not decoration, they are the instruction.

Interpolation and arithmetic

def item = 'coffee'
def cups = 3
def unitPrice = 2.5

println "Order: $cups cups of $item"
println "Total: ${cups * unitPrice}"
println 'Literal: $cups cups'

println 10 / 4         // 2.5 — division keeps the fraction
println 10.intdiv(4)   // 2 — whole-number division
println 10 % 4         // 2 — the remainder
println 2 ** 10        // 1024
println 0.1 + 0.2      // BigDecimal: exactly 0.3

Decimals that add up

A decimal written with a point is a BigDecimal in Groovy, and that choice is about exactness. BigDecimal stores digits in base ten, so 0.1 + 0.2 is exactly 0.3; a double stores a binary approximation, so adding two doubles that hold 0.1 and 0.2 gives 0.30000000000000004. Neither is wrong — they answer different questions.

The difference matters when values are added many times or compared for equality: money, totals, measurements that must reconcile. Declare double when you want speed and know the error is acceptable; keep the default BigDecimal when the last digit has to be believable. round(2) on a BigDecimal is the clean way to present a price.

A GString is a small program

A double-quoted string with a $ in it is a GString, not a String. It holds the pieces — literal text and expressions — and builds the final text when something asks for it. println asks, which is why printing works; getClass().simpleName shows GStringImpl and reminds you the value is still a recipe. It is also rebuilt each time it is read, so a value that changes between two prints changes the second line as well.

That matters at the edges: a map key or a method that insists on a String wants the finished text, so call .toString() when you hand a GString somewhere that stores it. Inside ordinary printing and interpolation you can forget the distinction entirely.

Three number traps: % keeps the sign of the left operand, so -7 % 3 is -1, not 2; intdiv truncates toward zero, so (-7).intdiv(2) is -3; and a double literal keeps binary error while a plain 0.1 is exact. When a comparison of decimals fails by a tiny amount, the type is the first thing to check.

Try these next

  • Print 0.1 + 0.2 as a BigDecimal and as a double and compare the two lines.
  • Format 19.99 * 3 with sprintf('%.2f', [...]).
  • Ask for the type of "$name" and of "$name".toString().

Greetings and arithmetic

Run it and compare the two halves: the interpolated lines and the plain arithmetic. Then change the score, and add a line that prints the total of two prices with braces and a sum inside.

Output
Click "Run" to execute your code

Exact, approximate and rounded

Run it and compare the two ways of adding 0.1 ten times. Then change the total to `0.1 * 3` as a plain literal and as a `double`, and explain the difference in one sentence.

Output
Click "Run" to execute your code

A $ in a double-quoted string is always interpolation. When you want to print a literal dollar sign, use single quotes, or escape it: "\$5" prints $5.

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Which string interpolates $name?