Groovy Variables
last modified June 26, 2026
Variables store values that a program can read and modify. Groovy supports both dynamic and static variable declarations, giving developers the flexibility of a scripting language alongside the safety of a typed one. This tutorial covers every major aspect of variables in Groovy.
Basic Definition
A variable is a named storage location that holds a value. In Groovy,
variables can be declared with the def keyword, with an explicit
type, or with var for local type inference. The type can change
at runtime when def is used.
Groovy compiles to JVM bytecode, so all Groovy types are ultimately Java types. Groovy adds dynamic dispatch and optional static typing on top of the Java type system.
def Keyword
The def keyword declares a variable whose type is determined at
runtime by the assigned value.
def name = "Alice" def age = 30 def weight = 58.5 def active = true println name println age println weight println active
Each variable is assigned a value and Groovy infers the runtime type from
that value. The def keyword signals that the type is not
constrained at the declaration site.
Using def is idiomatic Groovy for script-level code and method
local variables where the exact type is obvious from context.
$ groovy DefKeyword.groovy Alice 30 58.5 true
Explicit Type Declaration
Variables can be declared with a specific Java or Groovy type instead of
def. The type is then enforced at runtime.
String language = "Groovy" int year = 2003 double version = 4.0 boolean jvmBased = true println "$language $version was released around $year" println "JVM based: $jvmBased"
Explicit types document intent and allow IDEs and compilers to catch type mismatches early. Any value incompatible with the declared type triggers a runtime exception under normal Groovy compilation.
Common types are the standard Java primitives and their wrapper classes:
int, long, double,
boolean, String, BigDecimal.
$ groovy ExplicitTypes.groovy Groovy 4.0 was released around 2003 JVM based: true
Dynamic Typing
A def variable is not bound to a fixed type and can be
reassigned to a value of a different type.
def value = 42 println value.getClass().simpleName value = "Groovy" println value.getClass().simpleName value = [1, 2, 3] println value.getClass().simpleName
After each assignment, getClass reports the new runtime type.
The variable itself is just a reference that can point to any object.
Dynamic typing is convenient for prototyping and scripting. For production
code, explicit types or @CompileStatic are preferred.
$ groovy DynamicTyping.groovy Integer String ArrayList
var Keyword
Groovy 3 introduced var for local variable type inference,
similar to Java 10. The type is inferred from the initializer and is fixed
thereafter.
var message = "Hello, Groovy" var count = 100 var prices = [9.99, 14.50, 3.75] println message.toUpperCase() println count * 2 println prices.sum()
var behaves like a statically typed variable: the inferred type
is locked at declaration. Attempting to assign an incompatible value later
causes a compile error under @CompileStatic.
Use var when the type is obvious from the right-hand side and
you want the clarity of a fixed type without writing it out explicitly.
$ groovy VarKeyword.groovy HELLO, GROOVY 200 28.24
Multiple Assignment
Groovy supports destructuring assignment, allowing several variables to be declared and assigned from a list in a single statement.
def (first, second, third) = ["alpha", "beta", "gamma"] println first println second println third // Swap two variables def a = 1 def b = 2 (a, b) = [b, a] println "a=$a b=$b"
The list on the right is unpacked into the declared variables in order. If
the list is shorter than the variable list, the extra variables are assigned
null.
Multiple assignment is especially useful for swapping values and unpacking method return values without a temporary variable.
$ groovy MultipleAssignment.groovy alpha beta gamma a=2 b=1
String Variables and GStrings
Groovy strings enclosed in double quotes are GStrings that support
expression interpolation with the ${} syntax.
def firstName = "Jane"
def lastName = "Doe"
def age = 28
println "Name: $firstName $lastName"
println "Age: $age"
println "Born in: ${2026 - age}"
println "Upper: ${firstName.toUpperCase()}"
Simple variable references use $variable. Expressions and
method calls require the full ${expression} form.
Single-quoted strings are plain java.lang.String objects and
do not support interpolation. Triple-quoted variants of both exist for
multi-line strings.
$ groovy GStringExample.groovy Name: Jane Doe Age: 28 Born in: 1998 Upper: JANE
Null Value
Any variable can hold null, representing the absence of a
value. Groovy provides the safe-navigation operator ?. to
avoid NullPointerExceptions.
def username = null // Safe navigation - returns null instead of throwing NPE println username?.toUpperCase() // Elvis operator - provide a default for null def display = username ?: "Guest" println display // Null-safe assignment username = "alice" println username?.toUpperCase()
The ?. operator returns null when the receiver is
null rather than throwing an exception. The Elvis operator
?: returns the left operand if it is non-null, otherwise the
right operand.
$ groovy NullExample.groovy null Guest ALICE
Constants
The final keyword prevents a variable from being reassigned
after its initial assignment.
final double PI = 3.14159265358979 final int MAX_SIZE = 100 final String APP_NAME = "MyApp" println PI println MAX_SIZE println APP_NAME // PI = 3.14 // throws groovy.lang.ReadOnlyPropertyException
Attempting to reassign a final variable throws a
ReadOnlyPropertyException at runtime. For compile-time
enforcement, combine final with @CompileStatic.
Constants are typically written in UPPER_SNAKE_CASE when they represent fixed domain values or configuration, following Java conventions.
$ groovy Constants.groovy 3.14159265358979 100 MyApp
Variable Scope
Variables declared with def inside a class are instance fields.
Variables declared inside a method or block are local to that scope.
class Counter {
int count = 0 // instance variable
void increment() {
int step = 1 // local variable
count += step
}
void display() {
println "Count: $count"
}
}
def c = new Counter()
c.increment()
c.increment()
c.increment()
c.display()
The local variable step exists only within the
increment method. The instance variable count is
shared across all method calls on the same object.
$ groovy VariableScope.groovy Count: 3
Script Binding
In Groovy scripts, variables declared without def are stored in
the script's Binding object and are accessible across methods
in the same script.
message = "Hello from binding" // no def - goes into binding
def printMessage() {
println message // accessible here
}
printMessage()
def localVar = "I am local" // def - local to script body
println localVar
A variable without def in a script is a binding variable
visible to all closures and methods in that script. A variable with
def is local to the script's run method and not shared.
$ groovy ScriptBinding.groovy Hello from binding I am local
Primitive and Wrapper Types
Groovy automatically boxes primitive values into their wrapper types. Most Groovy code works with wrapper objects, though the compiler uses primitives internally for performance where possible.
int a = 10 // java.lang.Integer at runtime long b = 100_000L double c = 3.14 boolean d = false println a.getClass().simpleName println b.getClass().simpleName println c.getClass().simpleName println d.getClass().simpleName
Even though the variables are declared with primitive types, Groovy wraps
them in their object counterparts so that methods like
getClass can be called on them.
Underscores in numeric literals (e.g. 100_000L) are a Groovy
feature that improves readability for large numbers.
$ groovy PrimitiveTypes.groovy Integer Long Double Boolean
Type Coercion
The as operator converts a value to a specified type, invoking
Groovy's coercion mechanism.
def strNumber = "42" def number = strNumber as int println number + 8 def pi = 3.99 def truncated = pi as int println truncated def words = "Groovy" as List println words
Coercing a string to int parses the string. Coercing a
double to int truncates (not rounds) the value.
Coercing a string to List splits it into a list of characters.
$ groovy TypeCoercion.groovy 50 3 [G, r, o, o, v, y]
Static Type Checking
The @groovy.transform.TypeChecked annotation enables compile-time
type checking for a class or method, catching type errors before runtime.
import groovy.transform.TypeChecked
@TypeChecked
class Calculator {
int add(int a, int b) {
a + b
}
double average(List<Integer> values) {
values.sum() / values.size()
}
}
def calc = new Calculator()
println calc.add(10, 20)
println calc.average([4, 8, 15, 16, 23, 42])
@TypeChecked ensures that method calls and variable assignments
are type-safe at compile time. @CompileStatic goes further by
also generating optimised bytecode without dynamic dispatch.
$ groovy TypeChecked.groovy 30 18.0
Variable Naming
Groovy follows Java naming conventions. Variable and method names use camelCase. Constants use UPPER_SNAKE_CASE. Class names use PascalCase.
final int MAX_RETRIES = 3 def firstName = "Alice" def lastName = "Smith" def ageInYears = 30 def isActive = true println "$firstName $lastName, age $ageInYears, active: $isActive" println "Max retries: $MAX_RETRIES"
Descriptive names improve readability. Prefer isActive over
flag, userCount over n. Avoid
single-letter names except for short loop counters.
$ groovy VariableNaming.groovy Alice Smith, age 30, active: true Max retries: 3
Source
This tutorial covered Groovy variable declarations, dynamic and static typing, GStrings, null handling, scope, type coercion, and compile-time type checking.
Author
List all Groovy tutorials.