Functions in Groovy
Learn how to define and call functions in Groovy - parameters, default values, return values, recursion, closures, and higher-order functions with Docker-ready examples
Introduction
Functions are the building blocks of reusable code. They let you name a piece of behavior, pass data into it, and get a result back—turning long scripts into small, composable pieces. In Groovy, functions are remarkably flexible: you can write them with or without type annotations, omit the return keyword, give parameters default values, and treat blocks of code as values you can pass around.
Because Groovy is a multi-paradigm language—object-oriented, functional, imperative, and scripting all at once—it offers two closely related concepts: methods (defined with def or a return type) and closures (anonymous functions assigned to variables). Methods feel like the functions you know from Java or Python, while closures bring first-class functions, capturing their surrounding environment and enabling functional-style programming.
In this tutorial you’ll learn how to define and call methods, work with parameters and return values, supply default arguments, write recursive functions, understand variable scope, and harness closures as higher-order functions. Every example is a complete, runnable Groovy script you can execute with Docker.
Defining and Calling Methods
A method in a Groovy script is declared with either an explicit return type or the def keyword for dynamic typing. The return statement is optional—if you omit it, the value of the last expression is returned automatically.
Create a file named functions.groovy:
| |
Here square uses static types (int) for clarity and tooling support, while greet uses def and relies on Groovy’s implicit return—the GString "Hello, ${name}!" is the last expression, so it becomes the return value. Notice you call methods just like in Java, but parentheses can be omitted in many contexts.
Parameters, Defaults, and Varargs
Groovy makes parameter handling concise. You can give parameters default values so callers can omit them, and you can accept a variable number of arguments using varargs (the ... syntax).
Create a file named parameters.groovy:
| |
The ** operator is Groovy’s power operator, so power(3) computes 3 squared. With varargs, numbers arrives as an array, so sum can be called with any number of values—including none, which yields 0.
Recursion
A recursive function calls itself to solve a problem by breaking it into smaller subproblems. Groovy handles recursion naturally, and the optional ternary operator keeps the code compact.
Create a file named recursion.groovy:
| |
factorial(5) multiplies 5 × 4 × 3 × 2 × 1 = 120. The fib method returns n directly for the base cases (0 and 1) and otherwise sums the two preceding Fibonacci numbers. The (0..7).each { ... } line uses a range and a closure to print each result—a preview of the closure power covered below.
Variable Scope
Variables declared inside a method are local—they exist only while that method runs and are invisible elsewhere. Closures, however, can capture variables from the scope where they were created, keeping them alive afterward.
Create a file named scope.groovy:
| |
The factor variable cannot be seen outside scale. The interesting part is makeCounter: it returns a closure that still has access to count even after makeCounter has finished. Each call to counter() increments and returns the captured value, demonstrating a closure maintaining private state.
Closures and Higher-Order Functions
Closures are Groovy’s first-class functions: blocks of code you can store in variables, pass as arguments, and return from methods. A closure with a single parameter can use the implicit name it. Higher-order functions are methods or closures that accept or return other functions—the foundation of Groovy’s functional style.
Create a file named closures.groovy:
| |
applyTwice accepts a Closure and applies it twice—calling applyTwice(5) { it + 10 } adds 10 to get 15, then again to get 25. Groovy lets you pass a trailing closure outside the parentheses, which is why { it + 10 } sits after the call. The collection methods show idiomatic Groovy: collect transforms (map), findAll filters, and inject reduces a list to a single value.
Running with Docker
Run each example with the official Groovy image—no local Groovy installation required.
| |
On Windows PowerShell, replace $(pwd) with ${PWD}.
Expected Output
Running functions.groovy:
25
Hello, Groovy!
2026
Running parameters.groovy:
9
1024
15
0
Running recursion.groovy:
120
55
fib(0) = 0
fib(1) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
Running scope.groovy:
15
1
2
3
Running closures.groovy:
21
GROOVY
25
[1, 4, 9, 16, 25, 36]
[2, 4, 6]
21
Key Concepts
- Methods vs. closures: Methods are declared with
defor a return type; closures are anonymous functions assigned to variables. Closures are first-class values you can pass around. - Implicit return: The value of the last expression is returned automatically, so the
returnkeyword is optional. - Optional typing: Use
deffor dynamic typing or explicit types (int,String) for clarity and tooling—mix them freely in the same script. - Default parameters and varargs: Parameters can have default values, and
Type...accepts any number of arguments as an array, reducing the need for overloads. - Closures capture scope: A closure remembers the variables from where it was defined, enabling private state and patterns like counters and factories.
- Higher-order functions: Passing closures to methods like
collect,findAll, andinjectis the idiomatic Groovy way to transform, filter, and reduce collections. - The implicit
it: A single-parameter closure can reference its argument asitwithout declaring it explicitly. - Recursion is natural: Groovy supports recursion directly, and concise ternary expressions keep recursive definitions readable.
Running Today
All examples can be run using Docker:
docker pull groovy:4.0-jdk17-alpine
Comments
Loading comments...
Leave a Comment