Skip to content
Groovy Programming Language Guide — Scripting for the JVM

Groovy Programming Language Guide — Scripting for the JVM

DodaTech Updated Jun 7, 2026 9 min read

Groovy is a dynamic, optionally-typed language for the JVM that combines the power of Java with scripting productivity — featuring closures, builders, GStrings, and seamless Java integration.

What You’ll Learn

  • Groovy syntax and optional typing
  • Closures, GStrings, and the Groovy Development Kit (GDK)
  • AST transformations and metaprogramming
  • Gradle build scripts with Groovy DSL
  • Grails framework basics

Why It Matters

Groovy is the scripting language of the JVM — used by Gradle (the standard Android build system), Jenkins pipeline scripts, and Grails web applications. Durga Antivirus Pro uses Groovy for its automated testing and CI/CD pipeline configuration where readability and flexibility matter more than raw performance. Groovy makes Java development faster by removing boilerplate while keeping full compatibility with existing Java code and libraries. If you work with Gradle, Jenkins, or any JVM ecosystem, you’re already using Groovy.

Learning Path

    flowchart LR
  A[Groovy Basics<br/>You are here] --> B[Closures & Collections]
  B --> C[Metaprogramming & AST]
  C --> D[Gradle & Grails]
  D --> E[Build a Web App]
  

Your First Groovy Script

// hello.groovy
println "Hello, Groovy!"

def name = "World"
println "Hello, ${name}!"   // GString interpolation

// Semicolons are optional
// Return is optional (last expression is returned)
groovy hello.groovy
# Hello, Groovy!
# Hello, World!

Optional Typing and GStrings

Groovy offers both dynamic and static typing, plus powerful string interpolation.

// Dynamic typing (def)
def x = 42
x = "now a string"      // OK — dynamic

// Static typing (like Java)
int y = 100
// y = "hello"           // ERROR — type mismatch

// Strong typing with def
String z = "hello"
z = "world"              // OK
// z = 42                // ERROR

// GStrings — string interpolation
def name = "Alice"
def age = 30
def greeting = "Hello, ${name}! You are ${age} years old."
println greeting         // Hello, Alice! You are 30 years old.

// Multi-line strings
def json = """
{
    "name": "${name}",
    "age": ${age}
}
"""
println json

// Triple-quoted strings (preserve formatting)
def sql = '''\
SELECT *
FROM users
WHERE age > ${age}
ORDER BY name
'''
Hello, Alice! You are 30 years old.

{
    "name": "Alice",
    "age": 30
}

Closures

Closures are Groovy’s anonymous functions — similar to lambdas but more powerful.

// Basic closure
def square = { it * it }
println square(5)                 // 25

// Closure with explicit parameter
def greet = { String name ->
    "Hello, $name!"
}
println greet("Groovy")           // Hello, Groovy!

// Closure with multiple parameters
def add = { a, b -> a + b }
println add(10, 20)               // 30

// Closures have access to surrounding scope
def multiplier = 3
def triple = { it * multiplier }
println triple(7)                 // 21

// Closures as method arguments
def eachItem = [1, 2, 3, 4, 5]
eachItem.each { println it }      // prints each number

def doubled = eachItem.collect { it * 2 }
println doubled                   // [2, 4, 6, 8, 10]

def evens = eachItem.findAll { it % 2 == 0 }
println evens                     // [2, 4]

// Closure with implicit parameter: it
def names = ["Alice", "Bob", "Charlie"]
names.each { println "Name: $it" }
25
Hello, Groovy!
30
21
1
2
3
4
5
[2, 4, 6, 8, 10]
[2, 4]
Name: Alice
Name: Bob
Name: Charlie

GDK — Groovy Development Kit

The GDK extends Java’s standard library with dozens of convenience methods.

// Collection GDK enhancements
def numbers = [4, 2, 8, 1, 6, 3]

println numbers.sum()                 // 24
println numbers.max()                 // 8
println numbers.min()                 // 1
println numbers.average()             // 4.0
println numbers.count { it > 3 }      // 3
println numbers.groupBy { it % 2 == 0 ? "even" : "odd" }
// [even:[4, 2, 8, 6], odd:[1, 3]]

// String GDK enhancements
def str = "  Hello World  "
println str.trim()                    // "Hello World"
println str.padLeft(20)               // "        Hello World"
println str.contains("World")         // true
println str.tokenize()                // [Hello, World]
println "a,b,c,d".split(",")          // [a, b, c, d]
println "hello".capitalize()          // Hello

// File GDK enhancements
new File("test.txt").text = "Hello"    // write file
def content = new File("test.txt").text  // read file
println content                        // Hello

// Each line in file
new File("data.txt").eachLine { line ->
    println "Line: $line"
}

AST Transformations

Groovy compiles to Java bytecode. AST (Abstract Syntax Tree) transformations let you modify the compilation process.

import groovy.transform.*

// @ToString — auto-generates toString()
@ToString
class Person {
    String name
    int age
}

def p = new Person(name: "Alice", age: 30)
println p.toString()    // Person(Alice, 30)

// @Canonical — @ToString + @EqualsAndHashCode + @TupleConstructor
@Canonical
class Point {
    int x
    int y
}

def p1 = new Point(3, 4)
def p2 = new Point(3, 4)
println p1                     // Point(3, 4)
println p1 == p2               // true

// @Immutable — makes the class immutable
@Immutable
class Config {
    String host
    int port
}

def cfg = new Config("localhost", 8080)
// cfg.port = 9090             // ERROR — immutable

// @Singleton
@Singleton
class Database {
    String connect() { "Connected" }
}

println Database.instance.connect()  // Connected
Person(Alice, 30)
Point(3, 4)
true
Connected

Gradle Integration

Groovy is the native DSL for Gradle build scripts.

// build.gradle — Groovy DSL
plugins {
    id 'java'
    id 'application'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.google.guava:guava:33.0.0-jre'
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}

application {
    mainClass = 'com.example.Main'
}

tasks.named('test') {
    useJUnitPlatform()
}

// Custom task
tasks.register('greet') {
    doLast {
        println "Hello from Gradle!"
        println "Project: ${project.name}"
        println "Version: ${project.version}"
    }
}
gradle greet
# > Task :greet
# Hello from Gradle!
# Project: my-app
# Version: 1.0.0

Common Mistakes

1. Confusing == with .equals()

In Groovy, == calls .equals() automatically (and handles null). Java-style == for reference comparison uses .is(). This trips up Java developers migrating to Groovy.

2. Forgetting def or type in method parameters

def process(name, count) { ... }  // OK — dynamic
String process(name, count) { ... }  // ERROR — missing types on parameters
String process(String name, int count) { ... }  // OK  full types

3. Modifying collections while iterating

list.each { if (it > 3) list.remove(it) }  // ConcurrentModificationException

Use list.findAll { it <= 3 } (returns new list) instead.

4. Assuming closures are like Java lambdas

Closures can access and modify local variables in enclosing scope. Java lambdas require effectively-final variables. Closures also have delegate, owner, and thisObject for DSL building.

5. Using == for GString comparison with String

def gs = "Hello ${name}"
def s = "Hello Alice"
println gs == s   // true — Groovy coerces
println gs.equals(s)  // false  different types!

Use toString() on GStrings before comparison.

6. Not understanding def return type inference

Methods declared with def return Object. For better IDE support and type checking, declare return types explicitly: String greet() { ... }.

Practice Questions

  1. What is a GString? A string with embedded expressions: "Hello ${name}". Groovy compiles this to an interpolated string, different from Java’s String. GStrings use lazy evaluation.

  2. How do closures differ from methods? Closures are first-class values — can be passed as arguments, stored in variables, and capture their enclosing scope. Methods are owned by classes and cannot be passed directly.

  3. What are AST transformations? Annotations that modify the Abstract Syntax Tree during compilation. @ToString, @Canonical, @Immutable generate boilerplate code at compile time, not at runtime.

  4. How does Groovy compare to Java? Groovy is more concise (80% less boilerplate), supports dynamic typing, has closures and GStrings, and compiles to the same bytecode. Java has better performance and tooling support.

  5. What is the Groovy Development Kit (GDK)? A set of extension methods added to Java’s standard classes (String, Collection, File, etc.) that provide convenience operations like .each(), .collect(), .sum(), and .findAll().

Challenge: Write a Groovy script that reads a Maven pom.xml file, parses it using XmlSlurper, extracts all dependencies (groupId, artifactId, version), and prints them as a formatted table.

Mini Project — CSV Report Generator

@Canonical
class SaleRecord {
    String date
    String product
    int quantity
    BigDecimal price
    String region

    BigDecimal getTotal() { price * quantity }
}

def salesData = '''
2026-01-01,Widget A,10,19.99,North
2026-01-01,Widget B,5,29.99,South
2026-01-02,Widget A,8,19.99,East
2026-01-02,Widget C,15,9.99,North
2026-01-03,Widget B,12,29.99,West
2026-01-03,Widget A,6,19.99,South
2026-01-04,Widget C,20,9.99,East
'''

def records = salesData.trim().split('\n').collect { line ->
    def parts = line.split(',')
    new SaleRecord(
        date: parts[0],
        product: parts[1],
        quantity: parts[2] as int,
        price: parts[3] as BigDecimal,
        region: parts[4]
    )
}

println "=" * 60
println "SALES REPORT — Summary"
println "=" * 60

// Total sales by product
println "\n--- Sales by Product ---"
records.groupBy { it.product }.each { product, items ->
    def totalQty = items.sum { it.quantity }
    def totalRevenue = items.sum { it.total }
    println sprintf("%-12s Qty: %3d  Revenue: \$%8.2f", product, totalQty, totalRevenue)
}

// Total sales by region
println "\n--- Sales by Region ---"
records.groupBy { it.region }.each { region, items ->
    def totalRevenue = items.sum { it.total }
    println sprintf("%-8s Revenue: \$%8.2f", region, totalRevenue)
}

// Top selling product
def topProduct = records.groupBy { it.product }
    .collectEntries { k, v -> [k, v.sum { it.quantity }] }
    .max { it.value }

println "\n--- Top Selling Product ---"
println "${topProduct.key}: ${topProduct.value} units"

// Daily summary
println "\n--- Daily Summary ---"
records.groupBy { it.date }.each { date, items ->
    def totalRevenue = items.sum { it.total }
    def orderCount = items.size()
    println sprintf("%s  Orders: %d  Revenue: \$%8.2f", date, orderCount, totalRevenue)
}

println "\n" + "=" * 60
println "Generated: ${new Date().format('yyyy-MM-dd HH:mm')}"
println "=" * 60
============================================================
SALES REPORT — Summary
============================================================

--- Sales by Product ---
Widget A     Qty:  24  Revenue: $  479.76
Widget B     Qty:  17  Revenue: $  509.83
Widget C     Qty:  35  Revenue: $  349.65

--- Sales by Region ---
North    Revenue: $  349.75
South    Revenue: $  219.75
East     Revenue: $  359.70
West     Revenue: $  359.88

--- Top Selling Product ---
Widget C: 35 units

--- Daily Summary ---
2026-01-01  Orders: 2  Revenue: $  349.75
2026-01-02  Orders: 2  Revenue: $  259.70
2026-01-03  Orders: 2  Revenue: $  359.88
2026-01-04  Orders: 1  Revenue: $  199.80

============================================================
Generated: 2026-06-07 12:00
============================================================

FAQ

Is Groovy still relevant in 2026?
Absolutely. Groovy is the foundation of Gradle (the standard Android build system), Jenkins pipelines, and Grails. Even as Kotlin gains popularity, Groovy’s DSL capabilities and dynamic typing make it irreplaceable for build scripts and pipeline automation.
Should I learn Groovy or Kotlin?
If you work with Gradle build scripts or Jenkins pipelines, learn Groovy — it’s required. If you’re building new JVM applications, Kotlin is more modern and type-safe. Many developers learn both since they interoperate.
Can Groovy replace Java?
For scripting, testing, and DSLs, yes — Groovy is more productive. For large-scale enterprise applications, Java or Kotlin are better choices due to tooling, type safety, and performance. Groovy excels where flexibility matters more than raw speed.
What is Grails?
A full-stack web framework built on Groovy, inspired by Ruby on Rails. It uses Convention over Configuration, provides scaffolding, GORM (ORM), and integrates with Spring Boot. Grails is excellent for rapid web application development.
How does Groovy handle performance?
Groovy is slower than Java for computationally intensive code. For scripting, build files, and pipelines, the difference is negligible. For hot code paths, use @CompileStatic to make Groovy compile to the same bytecode as Java.
What is @CompileStatic?
An AST transformation that makes Groovy statically compiled — method dispatch, property access, and type inference happen at compile time instead of runtime. Performance matches Java’s, but some dynamic features (metaprogramming, runtime mixins) are disabled.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro