Groovy
Programming Language
Groovy
Overview
Groovy is a dynamic programming language that runs on the JVM, providing high compatibility with Java and more concise syntax.
Details
Groovy is a dynamic programming language developed by James Strachan in 2003 that runs on the JVM, maintaining full compatibility with Java while providing more concise and expressive syntax. It supports both static and dynamic typing, combining functional and object-oriented programming features. It can directly utilize Java libraries and seamlessly integrate with existing Java code. It provides many features that enhance developer productivity, including closures, operator overloading, string interpolation, and concise collection operations. It is widely adopted in Apache Gradle build tool, Jenkins CI/CD, Spring Framework, and other areas, being utilized in scripting, test automation, DSL (Domain Specific Language) construction, and web development.
Code Examples
Hello World
// Basic output
println "Hello, World!"
// Output using variables
def message = "Hello, Groovy!"
println message
// String interpolation
def name = "John"
def age = 25
println "My name is ${name} and I am ${age} years old."
// GString (Groovy String)
def template = """
Name: $name
Age: $age
Description: This is Groovy string interpolation.
"""
println template
// Java-style output is also possible
System.out.println("Java-compatible output")
Variables and Data Types
// Dynamic typing (def)
def dynamicVar = 42
dynamicVar = "changed to string"
dynamicVar = [1, 2, 3, 4, 5]
// Static typing
String staticString = "static typed string"
int staticInt = 100
boolean staticBoolean = true
// Primitive types
byte b = 1
short s = 100
int i = 1000
long l = 10000L
float f = 3.14f
double d = 3.14159
char c = 'A'
// Lists (dynamic arrays)
def list = [1, 2, 3, 4, 5]
def mixedList = [1, "string", true, 3.14]
def emptyList = []
// List operations
list << 6 // Add element
list += [7, 8, 9] // Add multiple elements
println "List: $list"
println "First element: ${list[0]}"
println "Last element: ${list[-1]}"
// Maps (associative arrays)
def map = [name: "John Doe", age: 30, city: "Tokyo"]
def emptyMap = [:]
// Map operations
map.job = "Engineer" // Add new key
map["hobby"] = "Reading" // Bracket notation
println "Map: $map"
println "Name: ${map.name}"
// Ranges
def range1 = 1..10 // 1 to 10 (inclusive)
def range2 = 1..<10 // 1 to 10 (exclusive)
def charRange = 'a'..'z'
println "Range1: $range1"
println "Character range: $charRange"
// Regular expressions
def pattern = ~/\d+/ // Groovy regex literal
def text = "I am 25 years old"
def matcher = text =~ pattern
println "Matched number: ${matcher[0]}"
Conditional Statements
def score = 85
// Basic if statement
if (score >= 90) {
println "Grade: A"
} else if (score >= 80) {
println "Grade: B"
} else if (score >= 70) {
println "Grade: C"
} else {
println "Grade: D"
}
// Ternary operator
def grade = score >= 80 ? "Pass" : "Fail"
println "Result: $grade"
// Elvis operator (null coalescing operator)
def name = null
def displayName = name ?: "Anonymous"
println "Display name: $displayName"
// Switch statement (Groovy extensions)
def value = "string"
switch (value) {
case String:
println "It's a string type"
break
case Integer:
println "It's an integer type"
break
case List:
println "It's a list type"
break
case ~/\d+/: // Regex matching
println "It's a numeric pattern"
break
default:
println "Other type"
}
// Switch with in operator
def number = 5
switch (number) {
case 1..5:
println "Range 1 to 5"
break
case [10, 15, 20]:
println "One of 10, 15, 20"
break
default:
println "Other"
}
// Safe navigation operator
def person = null
println "Name: ${person?.name}" // No error even if null
// Groovy Truth (Groovy's boolean evaluation)
if ("") println "Empty string is false" // Not executed
if ("string") println "String is true" // Executed
if ([]) println "Empty list is false" // Not executed
if ([1, 2, 3]) println "List is true" // Executed
if (0) println "0 is false" // Not executed
if (1) println "Non-zero is true" // Executed
Collections and Higher-Order Functions
// List higher-order functions
def numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// each (forEach equivalent)
numbers.each { num ->
println "Number: $num"
}
// eachWithIndex
numbers.eachWithIndex { num, index ->
println "Index $index: $num"
}
// collect (map equivalent)
def squares = numbers.collect { it * it }
println "Squares: $squares"
// find (first matching element)
def firstEven = numbers.find { it % 2 == 0 }
println "First even: $firstEven"
// findAll (filter equivalent)
def evenNumbers = numbers.findAll { it % 2 == 0 }
println "Even numbers: $evenNumbers"
// any (any element satisfies condition)
def hasLargeNumber = numbers.any { it > 5 }
println "Has number > 5: $hasLargeNumber"
// every (all elements satisfy condition)
def allPositive = numbers.every { it > 0 }
println "All positive: $allPositive"
// sum
def total = numbers.sum()
println "Total: $total"
// min/max
println "Min: ${numbers.min()}"
println "Max: ${numbers.max()}"
// sort
def sorted = numbers.sort { a, b -> b <=> a } // Descending
println "Sorted descending: $sorted"
// groupBy
def grouped = numbers.groupBy { it % 2 == 0 ? "even" : "odd" }
println "Grouped: $grouped"
// Map operations
def grades = [John: 85, Jane: 92, Bob: 78]
grades.each { name, grade ->
println "$name: $grade"
}
def highGrades = grades.findAll { name, grade -> grade >= 80 }
println "High grades: $highGrades"
// String operations
def text = "Hello, Groovy World!"
println "Uppercase: ${text.toUpperCase()}"
println "Split: ${text.split(' ')}"
// List comprehension style
def evenSquares = (1..10).findAll { it % 2 == 0 }.collect { it * it }
println "Even squares: $evenSquares"
Functions and Closures
// Basic function (method) definition
def greet(name) {
return "Hello, ${name}!"
}
// Return type specification
String greetTyped(String name) {
"Hello, ${name}!" // return can be omitted
}
// Default arguments
def calculateArea(width, height = 10) {
width * height
}
// Variable arguments
def sum(... numbers) {
numbers.sum()
}
// Closures (anonymous functions)
def square = { x -> x * x }
def add = { x, y -> x + y }
// Single parameter can use 'it'
def double = { it * 2 }
// Function taking closure as argument
def applyOperation(List list, Closure operation) {
list.collect(operation)
}
// Higher-order function
def createMultiplier(factor) {
return { number -> number * factor }
}
// Function usage examples
println greet("Alice")
println calculateArea(5) // height uses default value
println sum(1, 2, 3, 4, 5)
println "Square: ${square(5)}"
println "Add: ${add(10, 20)}"
def doubled = applyOperation([1, 2, 3, 4, 5], double)
println "Doubled: $doubled"
def tripler = createMultiplier(3)
println "Triple: ${tripler(7)}"
// Closure delegate
class Calculator {
def add(a, b) { a + b }
def multiply(a, b) { a * b }
}
def calc = new Calculator()
def operation = {
def result1 = add(5, 3)
def result2 = multiply(4, 6)
[result1, result2]
}
operation.delegate = calc
operation.resolveStrategy = Closure.DELEGATE_FIRST
println "Calculation result: ${operation()}"
// Recursive function
def factorial
factorial = { n ->
n <= 1 ? 1 : n * factorial(n - 1)
}
println "Factorial: ${factorial(5)}"
// Memoization (Groovy 2.2+)
def fibonacci
fibonacci = { n ->
n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2)
}.memoize()
println "Fibonacci: ${fibonacci(10)}"
Classes and Object-Oriented Programming
// Basic class definition
class Person {
String name
int age
private String id
// Constructor
Person(String name, int age) {
this.name = name
this.age = age
this.id = generateId()
}
// Method
def greet() {
"Hello, I'm ${name}."
}
def haveBirthday() {
age++
println "${name} is now ${age} years old."
}
private def generateId() {
"ID_${System.currentTimeMillis()}"
}
// toString
String toString() {
"Person(name: $name, age: $age)"
}
}
// Inheritance
abstract class Animal {
String name
Animal(String name) {
this.name = name
}
abstract def makeSound()
def introduce() {
"I'm an animal named ${name}."
}
}
class Dog extends Animal {
String breed
Dog(String name, String breed) {
super(name)
this.breed = breed
}
def makeSound() {
"Woof"
}
def fetch() {
"${name} fetched the ball."
}
}
// Interfaces
interface Flyable {
def fly()
}
interface Swimmable {
def swim()
}
// Multiple interface implementation
class Duck extends Animal implements Flyable, Swimmable {
Duck(String name) {
super(name)
}
def makeSound() {
"Quack"
}
def fly() {
"${name} is flying"
}
def swim() {
"${name} is swimming in the pond"
}
}
// Traits (Groovy 2.3+)
trait Debuggable {
def debug(message) {
println "[DEBUG] ${this.class.simpleName}: $message"
}
}
trait Timestamped {
Date created = new Date()
def getAge() {
(System.currentTimeMillis() - created.time) / 1000
}
}
// Class using traits
class Service implements Debuggable, Timestamped {
String name
Service(String name) {
this.name = name
}
def doWork() {
debug("Starting work: $name")
// Some processing
debug("Work completed")
}
}
// Groovy properties (automatic getter/setter generation)
class Book {
String title
String author
BigDecimal price
// Custom getter
String getDisplayTitle() {
"\"$title\""
}
// Custom setter
void setPrice(BigDecimal price) {
if (price < 0) {
throw new IllegalArgumentException("Price must be non-negative")
}
this.price = price
}
}
// Usage examples
def person = new Person("John Doe", 30)
println person.greet()
person.haveBirthday()
def animals = [
new Dog("Buddy", "Golden Retriever"),
new Duck("Donald")
]
animals.each { animal ->
println animal.introduce()
println "Sound: ${animal.makeSound()}"
if (animal instanceof Flyable) {
println animal.fly()
}
}
def service = new Service("Test Service")
service.doWork()
println "Service age: ${service.age} seconds"
def book = new Book(title: "Groovy in Action", author: "John Smith", price: 2500)
println "Book: ${book.displayTitle} by ${book.author}"
File Operations and I/O
// File reading and writing
def fileName = "sample.txt"
// Write to file
new File(fileName).text = """This is a test file.
Learning file operations in Groovy.
Can write multiple lines of text."""
// Read file
def content = new File(fileName).text
println "File content:\n$content"
// Read line by line
new File(fileName).eachLine { line, lineNumber ->
println "Line $lineNumber: $line"
}
// File existence check and operations
def file = new File(fileName)
if (file.exists()) {
println "File size: ${file.size()} bytes"
println "Last modified: ${new Date(file.lastModified())}"
}
// CSV data processing
def csvData = """Name,Age,Job
John Doe,30,Engineer
Jane Smith,25,Designer
Bob Johnson,35,Manager"""
new File("employees.csv").text = csvData
// Simple CSV parser implementation
def employees = []
new File("employees.csv").eachLine { line, lineNumber ->
if (lineNumber > 1) { // Skip header
def (name, age, job) = line.split(',')
employees << [name: name, age: age as Integer, job: job]
}
}
employees.each { emp ->
println "${emp.name} (${emp.age}) - ${emp.job}"
}
// Directory operations
def dir = new File(".")
dir.eachFile { file ->
println "${file.name} (${file.isDirectory() ? 'directory' : 'file'})"
}
// Groovy's @Grab (dynamic dependency resolution)
@Grab('org.apache.commons:commons-lang3:3.12.0')
import org.apache.commons.lang3.StringUtils
def text = " Hello, World! "
println "Trimmed: '${StringUtils.strip(text)}'"
// JSON processing
@Grab('org.apache.groovy:groovy-json:4.0.0')
import groovy.json.*
def jsonData = [
name: "API Response",
data: [
[id: 1, title: "Item 1"],
[id: 2, title: "Item 2"]
],
status: "Success"
]
def jsonString = JsonOutput.toJson(jsonData)
println "JSON format:\n${JsonOutput.prettyPrint(jsonString)}"
def parsedJson = new JsonSlurper().parseText(jsonString)
println "Parsed name: ${parsedJson.name}"
Special Features
Metaprogramming
// Dynamic method addition
String.metaClass.isPalindrome = {
delegate == delegate.reverse()
}
println "'racecar'.isPalindrome(): ${'racecar'.isPalindrome()}"
// Dynamic property addition
Integer.metaClass.getSquare = {
delegate * delegate
}
println "5 squared: ${5.square}"
// MethodMissing (handling non-existent methods)
class DynamicClass {
def methodMissing(String name, args) {
"Called method: $name, args: $args"
}
def propertyMissing(String name) {
"Non-existent property: $name"
}
}
def dynamic = new DynamicClass()
println dynamic.someMethod("arg1", "arg2")
println dynamic.someProperty
// Categories (temporary metaclass changes)
class StringCategory {
static String rot13(String self) {
self.tr('A-Za-z', 'N-ZA-Mn-za-m')
}
}
use(StringCategory) {
println "'Hello'.rot13(): ${'Hello'.rot13()}"
}
// Expando (dynamic object)
def expando = new Expando()
expando.name = "Dynamic Object"
expando.greet = { "Hello, ${name}!" }
expando.age = 25
println expando.greet()
// AST Transformations (@Delegate example)
class CarEngine {
def start() { "Engine started" }
def stop() { "Engine stopped" }
}
class Car {
@Delegate CarEngine engine = new CarEngine()
String model
Car(String model) {
this.model = model
}
}
def car = new Car("Prius")
println car.start() // CarEngine methods are delegated
DSL (Domain Specific Language) Construction
// Builder pattern
class HtmlBuilder {
def level = 0
def methodMissing(String name, args) {
def indent = " " * level
if (args.length == 1 && args[0] instanceof Closure) {
println "${indent}<$name>"
level++
args[0]()
level--
println "${indent}</$name>"
} else {
println "${indent}<$name>${args[0]}</$name>"
}
}
}
def html = new HtmlBuilder()
html.html {
head {
title "Groovy DSL Example"
}
body {
h1 "Welcome to Groovy"
p "This is a DSL example"
div {
span "Nested content"
}
}
}
// Custom DSL example
class DatabaseDSL {
def queries = []
def select(String... columns) {
queries << "SELECT ${columns.join(', ')}"
this
}
def from(String table) {
queries << "FROM $table"
this
}
def where(String condition) {
queries << "WHERE $condition"
this
}
def build() {
queries.join(' ')
}
}
def query = new DatabaseDSL()
.select("name", "age")
.from("users")
.where("age > 18")
.build()
println "Generated SQL: $query"
// Configuration DSL
class ConfigDSL {
def config = [:]
def database(Closure closure) {
def dbConfig = [:]
closure.delegate = dbConfig
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure()
config.database = dbConfig
}
def server(Closure closure) {
def serverConfig = [:]
closure.delegate = serverConfig
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure()
config.server = serverConfig
}
def propertyMissing(String name, value) {
this[name] = value
}
}
def configDsl = new ConfigDSL()
configDsl.with {
database {
host = "localhost"
port = 5432
name = "myapp"
}
server {
host = "0.0.0.0"
port = 8080
ssl = true
}
}
println "Configuration: ${configDsl.config}"
Grape (Dependency Management) and Scripts
// Grape automatic dependency resolution
@Grab('org.apache.commons:commons-csv:1.9.0')
import org.apache.commons.csv.*
// Advanced CSV processing
def csvData = """Name,Age,City
John,25,Tokyo
Jane,30,Osaka
Bob,35,Kyoto"""
def format = CSVFormat.DEFAULT.withFirstRecordAsHeader()
def parser = CSVParser.parse(csvData, format)
parser.forEach { record ->
println "${record.get('Name')} (${record.get('Age')}) from ${record.get('City')}"
}
// REST client example
@Grab('org.apache.groovy:groovy-json:4.0.0')
import groovy.json.JsonSlurper
// Simple HTTP client
def url = "https://jsonplaceholder.typicode.com/posts/1"
def connection = new URL(url).openConnection()
def response = connection.inputStream.text
def json = new JsonSlurper().parseText(response)
println "Title: ${json.title}"
println "Body: ${json.body}"
// One-liner script examples
// groovy -e "println 'Hello from command line'"
// groovy -e "(1..10).each { println it * it }"
// groovy -e "new File('.').eachFile { println it.name }"
Versions
Version | Status | Key Features | Release Year |
---|---|---|---|
Groovy 4.0 | Latest | Java 17 support, performance improvements | 2022 |
Groovy 3.0 | Current | Parrot parser, Java 14 support | 2020 |
Groovy 2.5 | Maintained | Macro support, Android compatibility improvements | 2018 |
Groovy 2.4 | Legacy | Traits introduction, annotation improvements | 2014 |
Reference Pages
Official Documentation
- Apache Groovy - Official site
- Groovy Documentation - Official documentation
- Groovy API - API reference
Learning Resources
- Groovy in Action - Comprehensive learning book
- Mr. Haki's Groovy Goodness - Groovy techniques collection
- Groovy Web Console - Online execution environment
Frameworks and Tools
- Gradle - Build automation tool
- Grails - Web application framework
- Spock Framework - Testing framework