๐Ÿƒ Groovy Core Philosophy

๐ŸŽฏ The Agile JVM Language

Groovy (James Strachan, 2003) is a powerful, optionally typed, dynamic language for the JVM with seamless Java integration. It combines Python/Ruby-like conciseness with Java's robustness. Key pillars: closures, builders, metaprogramming, and DSL support. Groovy compiles to Java bytecode and runs anywhere Java runs.

Deep Explanation

Groovy is both dynamic and static. With @CompileStatic, you get Java-like performance. Without, you leverage dynamic method dispatch, runtime metaprogramming, and categories. Groovy's Multi-methods dispatch based on runtime types. The GDK extends Java's core classes with hundreds of convenience methods.

Why it matters: Groovy reduces boilerplate by 50-80% compared to Java, making it ideal for scripts, build systems (Gradle), testing (Spock), and internal DSLs.

๐Ÿ“Œ Optional Typing๐Ÿ“Œ Metaprogramming๐Ÿ“Œ Seamless Java Interop๐Ÿ“Œ CompileStatic Mode
// Groovy's conciseness vs Java // No semicolons, optional parentheses, optional types def name = "Groovy" println "Hello, $name" // GString interpolation // Dynamic list and map literals def list = [1, 2, 3] def map = [name: "Groovy", type: "JVM"] println list.class // java.util.ArrayList // Safe navigation operator def person = null println person?.name ?: "Anonymous" // Elvis operator

๐ŸŽญ Dynamic & Static Nature

๐ŸŽฏ def vs @CompileStatic

Groovy is dynamic by default but supports static compilation via annotations. Type checking with @TypeChecked.

Dynamic Dispatch vs Static Safety

Dynamic mode enables runtime method addition (ExpandoMetaClass), methodMissing, and propertyMissing. Static mode catches errors at compile time and performs Java-like performance. Use @CompileStatic for critical paths.

๐Ÿ”„ Runtime metaprogrammingโœ… @TypeCheckedโšก @CompileStatic
// Dynamic: add methods at runtime String.metaClass.reverseAndShout = { -> delegate.reverse().toUpperCase() } println "hello".reverseAndShout() // OLLEH // Static compilation import groovy.transform.CompileStatic @CompileStatic int add(int a, int b) { a + b } // Type checking import groovy.transform.TypeChecked @TypeChecked void safeMethod(String s) { // println s.length() works; but dynamic dispatch disabled }

๐Ÿ”’ Closures & Lambdas

๐ŸŽฏ First-Class Functions

Closures are anonymous blocks with lexical binding, currying, delegation strategy (OWNER_FIRST, DELEGATE_FIRST).

Closure Magic

Closures remember their surrounding context (owner, delegate, this). They support currying (.curry()), trampoline for recursion, and method pointers (&.methodName). Used heavily in Gradle and collections.

๐ŸŽฏ Closure delegation๐Ÿ“ฆ Currying๐Ÿ” Trampoline
// Basic closure def greet = { name -> "Hello, $name" } println greet("Groovy") // Currying def multiply = { a, b -> a * b } def doubleIt = multiply.curry(2) println doubleIt(5) // 10 // Collection iteration with closure [1,2,3].collect { it * 2 } // [2,4,6] // Method pointer def strings = ["a", "bb", "ccc"] println strings.collect(&.toUpperCase)

๐Ÿ“š GDK Collections Power

๐ŸŽฏ Enriched Collection API

Java Collections extended with each, findAll, collect, inject, groupBy, countBy and many more.

Functional Style

Groovy's GDK adds functional methods to all collections. findAll filters, collect maps, inject reduces, groupBy organizes. These are lazy with .iterator() or eager by default.

๐Ÿ”„ Functional Chaining๐Ÿ“Š groupBy/countByโ™ป๏ธ Lazy Iterators
def numbers = 1..10 def evens = numbers.findAll { it % 2 == 0 } // [2,4,6,8,10] def squares = numbers.collect { it * it } def sum = numbers.inject(0) { acc, val -> acc + val } def fruits = ['apple', 'banana', 'apple', 'orange'] def countMap = fruits.countBy { it } // [apple:2, banana:1, orange:1] // Nested collections def matrix = [[1,2],[3,4]] println matrix.flatten() // [1,2,3,4]

๐Ÿ“ GStrings & Multiline

๐ŸŽฏ String Interpolation

GString ("Hello $name"), multiline triple quotes ('''text''' or """text"""), slashy strings for regex.

Flexible String Handling

GStrings are lazy-evaluated: expressions inside ${} are re-evaluated when the GString is used in certain contexts. Multiline strings preserve formatting. Slashy strings (/regex/) simplify regex patterns.

โœจ Lazy interpolation๐Ÿ“„ Triple quotes๐Ÿ” Slashy regex
def name = "Groovy" def greeting = "Hello, ${name.toUpperCase()}" println greeting // Hello, GROOVY // Multiline def poem = """ Roses are red, Violets are blue, Groovy is sweet, And so are you. """ // Slashy string for regex def pattern = /(\w+)@(\w+)\.\w+/ def matcher = "user@example.com" =~ pattern println matcher[0][1] // user

๐Ÿ”ง Operator Overloading & Elvis

๐ŸŽฏ Groovy Operators

Elvis ?:, safe navigation ?., spaceship <=>, spread *., and overloadable operators.

Expressive Syntax

Elvis operator returns default value when left side is null. Spread operator calls method on all collection elements. All operators map to method names (plus, minus, getAt, putAt).

๐ŸŽธ Elvis ?:๐Ÿ›ก๏ธ Safe nav ?.โœจ Spread *.๐Ÿ“ Spaceship <=>
// Elvis operator def value = null def result = value ?: "default" // Safe navigation def person = [address: null] def city = person?.address?.city ?: "Unknown" // Spread operator def words = ["Groovy", "Closure"] println words*.toUpperCase() // [GROOVY, CLOSURE] // Overloading + for custom class class Complex { int real, imag Complex plus(Complex other) { new Complex(real + other.real, imag + other.imag) } }

๐Ÿ”€ Groovy Control Structures

๐ŸŽฏ Enhanced Syntax

switch with any type, for-in loops, assert with power asserts, with(), use() categories.

Smart Control Flow

Groovy's switch supports any object (String, Class, regex patterns). Power asserts display detailed expression trees. with() eliminates repetitive obj. prefixes.

๐ŸŽฏ Switch with patterns๐Ÿ“Š Power asserts๐Ÿ—‚๏ธ with() scope
// Switch on any type def checkType(value) { switch(value) { case String: return "String" case ~/\d+/: return "Number string" case 0..100: return "Between 0-100" default: return "Other" } } println checkType("42") // Number string // Power assert def x = 5 assert x * 2 == 11 : "Should be 10" // with() for grouping def sb = new StringBuilder() sb.with { append("Groovy ") append("is ") append("awesome") }

๐Ÿ›๏ธ POGO & Metaprogramming

๐ŸŽฏ Plain Old Groovy Objects

Automatic property accessors, @Canonical, @Builder, @Delegate, runtime metaclass modifications.

AST Transformations

Groovy's compile-time metaprogramming (AST transforms) injects boilerplate code. @ToString, @EqualsAndHashCode, @TupleConstructor reduce verbosity. ExpandoMetaClass enables runtime method injection.

๐Ÿ“ฆ @Canonical๐Ÿ—๏ธ @Builder๐Ÿ”ง ExpandoMetaClass
import groovy.transform.* @Canonical @Builder class Developer { String name int years } def dev = Developer.builder().name("Alice").years(5).build() println dev // Developer(Alice, 5) // Runtime metaclass String.metaClass.scream = { -> delegate.toUpperCase() + "!!!" } println "groovy".scream() // GROOVY!!!

๐Ÿ“œ DSL & Builders

๐ŸŽฏ Internal DSLs

Groovy's builder pattern: MarkupBuilder, JsonBuilder, SwingBuilder, and custom DSLs using methodMissing and propertyMissing.

Creating Fluent APIs

Builders transform nested closures into object structures. MarkupBuilder generates XML/HTML. Command chains allow natural language DSLs.

๐Ÿ“ MarkupBuilder๐Ÿ”ง Custom DSLsโšก Command chains
import groovy.xml.MarkupBuilder def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.team { member(role: "lead") { name("Alice") } member(role: "dev") { name("Bob") } } println writer.toString() // Command chain (natural language) def move = { args -> println "Moving to $args" } def forward = { move("forward") } def backward = { move("backward") }

๐Ÿ“„ XML/JSON Slurpers

๐ŸŽฏ GPath & Parsing

XmlSlurper, JsonSlurper provide XPath-like navigation (GPath) for easy data extraction.

Effortless Parsing

XmlSlurper handles large XML lazily. JsonSlurper converts JSON to native Groovy maps/lists. GPath syntax: xml.book[0].author.

๐Ÿ” GPath navigation๐Ÿ“ฆ Lazy parsing๐Ÿ”„ JSON conversion
import groovy.json.JsonSlurper import groovy.xml.XmlSlurper // JSON slurper def jsonText = '{"name":"Groovy", "version":4.0}' def data = new JsonSlurper().parseText(jsonText) println data.name // XML slurper def xml = '''Groovy in Action''' def parsed = new XmlSlurper().parseText(xml) println parsed.book.title.text()

โ˜• Groovy-Java Integration

๐ŸŽฏ Seamless Interop

Groovy can import Java classes, extend Java classes, implement Java interfaces. Java can call Groovy via scripting engine or compiled classes.

Two-Way Integration

From Java, use GroovyScriptEngine or compile Groovy to .class. From Groovy, all Java libraries are available without wrappers. Groovy closures automatically convert to SAM interfaces (Single Abstract Method).

๐Ÿ”— Closures as SAMs๐Ÿ“ฆ groovy-all JARโšก ScriptEngine
// Groovy using Java's CompletableFuture import java.util.concurrent.CompletableFuture def future = CompletableFuture.supplyAsync { "Hello from Groovy" } println future.get() // Java can call Groovy script (in Java code) // ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy"); // engine.eval("println 'Hello Java'"); // SAM conversion: Groovy closure as Runnable Runnable task = { println "Running in thread" }

๐Ÿ‡ Grape Dependency Manager

๐ŸŽฏ @Grab Annotations

Declarative dependency fetching from Maven Central/Ivy. Resolves transitive dependencies at runtime.

Zero-Build Scripts

@Grab avoids build files. Perfect for scripts and one-off tools. Grape caches artifacts locally, supports custom resolvers.

๐Ÿ“ฆ @Grab decorator๐Ÿ’พ Local cacheโšก Transitive deps
// Script with dependencies, no build.gradle needed! /* @Grab(group='org.apache.commons', module='commons-math3', version='3.6.1') */ import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics def stats = new DescriptiveStatistics() [1,2,3,4,5].each { stats.addValue(it) } println "Mean: ${stats.mean}, Std: ${stats.standardDeviation}"

๐Ÿงช Spock & Testing

๐ŸŽฏ Specification Framework

Spock: given-when-then blocks, mocking, data-driven testing. Built on JUnit.

Expressive Tests

Spock's blocks create readable tests. Data-driven tests with where: input | expected simplify parameterization. Interaction-based mocking replaces traditional mocking frameworks.

๐Ÿ“‹ given/when/then๐Ÿ“Š Data tables๐ŸŽญ Mock interactions
// Spock specification example /* def "addition should work"() { given: def calculator = new Calculator() expect: calculator.add(a, b) == result where: a | b || result 1 | 1 || 2 2 | 3 || 5 } */ // Groovy's built-in assertion is test-friendly assert [1,2,3].sum() == 6

โฉ GPars & Concurrency

๐ŸŽฏ Parallelism for Groovy

GPars (Groovy Parallel Systems) provides actors, dataflow, fork/join, and parallel collections.

High-Level Concurrency

GPars simplifies multi-threading with GParsPool for parallel collection operations, actors for message-passing, and dataflow for reactive streams. Works with Groovy's closures seamlessly.

๐Ÿ”„ Parallel collections๐ŸŽญ Actor model๐Ÿ“ก Dataflow vars
import static groovyx.gpars.GParsPool.withPool def numbers = 1..1000 withPool(4) { def results = numbers.collectParallel { it * it } def sumParallel = numbers.sumParallel() println "Sum: $sumParallel" } // Actor example (simplified) // def actor = Actors.actor { loop { react { println it } } } // actor.send("Hello")