This episode dissects Groovy's architecture: program structure, the execution model on the JVM, compilation into bytecode, and two-way interoperability with Java. You will also run Groovy from within a Java application using GroovyShell.

After understanding Groovy's history and position in the JVM ecosystem, it's time to dissect the language architecture — how Groovy works behind the scenes. This understanding matters because it explains why Groovy can be dynamic and, at the same time, compatible with Java.
Episode 2 covers Groovy's program structure, its execution model on the JVM, the bytecode compilation process, and two-way interoperability between Groovy and Java. By the end of the episode you'll be able to run Groovy code from within a Java application and vice versa.
Groovy's program structure depends on how you run it. There are three main forms:
.groovy file without a class declaration, executed directly from top to bottom..groovy file containing a Groovy class declaration, similar to a Java file.When Groovy runs a script, it automatically wraps it into a class named Script, and every statement becomes the body of a run method. Variables declared at script level become properties of that class.
This model creates an interesting property: variables defined in one part of a script remain accessible in other parts, because everything lives in a single run scope. Here's an example of a simple script structure:
def nama = "Arman"
def umur = 25
def perkenalan() {
return "Nama: ${nama}, Umur: ${umur}"
}
println perkenalan()The script above defines two variables and one method, then calls it. return "Nama: ${nama}, Umur: ${umur}" leverages GString to interpolate variable values — a topic we'll dive into in episode 3.
Groovy has two main execution paths. First, Groovy compiles code into bytecode with groovyc, producing .class files that can be run by the JVM like ordinary Java classes. Second, a script can be run directly with the groovy command, which performs compilation automatically behind the scenes.
groovyc halo.groovy
ls *.classThe groovyc halo.groovy command produces one or more .class files in the same directory. Those files can be run with java if a main class is found, or bundled into a larger Java project.
Unlike Java, Groovy compilation preserves dynamic dispatch. When a method is called, Groovy looks up the appropriate implementation at runtime, not at compile time. This is what enables metaprogramming — method behavior can be changed or added after compilation.
As a result, Groovy bytecode is larger and slightly slower than pure Java bytecode. Episode 16 will cover this trade-off in depth, including how to close it with @CompileStatic.
For scripting needs inside an application, Groovy provides GroovyShell. This is the primary API for evaluating Groovy expressions or scripts from within Java code. Here's an example:
def shell = new GroovyShell()
def hasil = shell.evaluate("1 + 2 * 3")
println hasilshell.evaluate("1 + 2 * 3") executes a Groovy string and returns its result, which is 7. new GroovyShell() creates an evaluation engine instance that can be reused repeatedly with the same variable binding.
For interactive exploration, Groovy provides two tools: groovyConsole, which is GUI-based, and groovysh, which is terminal-based. Both use the same engine as GroovyShell, so the results you see in the REPL will be consistent with production behavior.
Because it runs on the JVM, Groovy can use any Java library without adapters. Importing Java packages works directly, including access to enums, static methods, and generic types.
import java.time.LocalDate
def hariIni = LocalDate.now()
println "Tanggal hari ini: ${hariIni}"import java.time.LocalDate shows that Groovy's import syntax is identical to Java. LocalDate.now() is called directly, and the result can be interpolated into a GString without boilerplate.
Interoperability also works in the opposite direction. Groovy jars implement GroovyObject and are compatible with Java's reflection mechanism, so Java applications can use Groovy classes directly. For dynamic script evaluation, Java uses GroovyShell:
import groovy.lang.GroovyShell;
public class Main {
public static void main(String[] args) {
GroovyShell shell = new GroovyShell();
Object hasil = shell.evaluate("2 + 2");
System.out.println(hasil);
}
}To run the Java code above, make sure groovy-4.0.24.jar is on the classpath. shell.evaluate("2 + 2") inside the Java code calls the same Groovy engine, proving that Groovy and Java live in one JVM process without restrictions.
There are two patterns for using Groovy in a Java application:
GroovyShell or GroovyClassLoader, flexible but slower and requiring security attention.We'll revisit the runtime pattern in episode 20, especially the sandboxing aspects for scripts that come from untrusted sources.
Episode 2 explained how Groovy works internally: scripts are wrapped into a Script class, compiled into JVM bytecode, preserve dynamic dispatch at runtime, and interact with Java in both directions through GroovyShell.
The key takeaways:
Script class by the engine.groovyc produces .class bytecode that Java can use.GroovyShell is the primary API for evaluating scripts from a Java application.In episode 3 next, we'll discuss basic syntax and data types — dynamic variables with def, primitive types, strings, lists, maps, GString, multi-line strings, and string operators. These are the basic vocabulary used in every episode that follows.