Groovy from the ground up

Values, def and types

Two ways to declare a variable — letting the value decide with def, or writing the type down — and what each one buys you when the program grows.

Part 2 of 12

Beginner14 min readBack to Courses

def lets the value decide

def name = 'Ada' declares a variable and lets the value on the right decide its type. The variable is still typed — Groovy is not JavaScript — but the type comes from the value instead of from you, so nothing is repeated on the line. Underneath, the variable is an Object reference in the compiled class and the compiler records no type for it, so the check happens when the line runs rather than when the file is compiled.

The types you will meet most are String for text, Integer for whole numbers and BigDecimal for decimals written with a point. That last one surprises people coming from other languages: 0.75 is a BigDecimal in Groovy, not a double, because a language that ends up handling money prefers decimals that add up exactly.

Writing the type down

You can also name the type: int pages = 300, String title = 'Dune', boolean ready = true. The type is then checked against the value — putting text into an int stops the program with a cast error on that line, rather than succeeding quietly — and the reader of the code knows what is coming without looking further. A written type is also what a static checker and an editor can read, so the declaration that documents the value to a person also gives a tool something to verify.

Both styles are ordinary Groovy and both appear in real code. The usual advice: use a written type where the type is part of the meaning (a page count, a price, a flag), and def where the right-hand side already says it plainly or where the type is genuinely incidental — a loop counter, a temporary.

The same value, two meanings

Types matter because + behaves differently depending on them. Two numbers add; anything with a string joins. 1 + 2 is 3, '1' + 2 is 12, and 7 / 2 is 3.5 because Groovy divides honestly even when both sides are whole numbers. The pair 1 + 2 and "1" + 2 look almost the same on the page and differ only in the quotes; that is the smallest example of why a value’s type is part of what the code means.

When you are unsure what a value is, ask it: value.getClass().simpleName answers String, Integer, BigDecimal and so on. It is the fastest way to end an argument with yourself about what the last line produced.

Declaring values two ways

def anything = 'text'      // the value decides the type
def count = 3
def price = 2.5

int pages = 300            // a type, written down
String title = 'Dune'
boolean inPrint = true

println anything.getClass().simpleName
println count.getClass().simpleName
println price.getClass().simpleName
println pages + 4
println title.toUpperCase()
println inPrint

// Text plus a number is text; numbers add up.
println 'pages: ' + (pages + 4)
println 'pages: ' + pages + 4

What def leaves to run time

def does not remove the type; it moves the decision to run time. The compiler records the variable as a reference without a fixed type, and each value carries its own class, so total + 1 is resolved when the line runs — which is why a def variable can hold an Integer now and a String later.

A written type pins the variable down. int total = 3 fixes total as a whole number, and assigning text to it fails at that assignment instead of somewhere later. The trade is real: def is flexible and reports mistakes late, a written type is rigid and reports them where they happen.

Conversion is a method, not a wish

Text and numbers do not convert by looking alike. '42' is a string of two characters, and + joins it to whatever follows; 42 is a number, and + adds. To cross the gap, ask for the conversion: '42'.toInteger(), 3.5.toString(), '2.50'.toBigDecimal().

Each of those can fail. 'forty'.toInteger() throws NumberFormatException, and converting a decimal to an integer loses the fraction rather than rounding. When the text comes from outside the program, check it before converting; when it comes from your own code, the conversion documents what you meant.

Three type mistakes are worth recognising. 7 / 2 is 3.5 because Groovy divides honestly, so use intdiv for a whole-number quotient. '7' + 2 is the text 72, not 9, because a string on either side turns + into joining. And a def variable can be reassigned to another type, so a mistake may not surface until a later line uses the old assumption.

Try these next

  • Print the type of 1, 1.0, 1G, 1L and '1' with getClass().simpleName.
  • Convert '19.99' to a BigDecimal, multiply it by 3, and print the exact answer.
  • Declare int count = 3, try to assign 'three' to it, and read the error.

Ask six values what they are

Run it and read the six type names. Then add a value of your own with `def` and one with a written type, and print the type of each.

Output
Click "Run" to execute your code

Text in, numbers out

Run it and watch two strings become a quantity and a price. Then change `quantityText` to `'seven'` and read the failure, before putting it back.

Output
Click "Run" to execute your code

simpleName is deliberate here: a full type name in Groovy carries its package, so getClass().name would print java.math.BigDecimal. simpleName prints the part you were asking about.

Check what you learned

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

Part 1 of 5

What does def total = 3 decide?