Apache Groovy adalah bahasa pemrograman dinamis yang berjalan di atas Java Virtual Machine (JVM), mesin virtual yang juga menjalankan program Java. Topik ini memperkenalkan posisi Groovy di JVM, interoperabilitasnya dengan Java, keringkasan sintaksnya, peta versi yang berlaku, serta batas antara kode dinamis dan kode terkompilasi statis. Setiap pernyataan perilaku disertai skrip yang benar-benar dijalankan pada Groovy 5.1.3 di atas JDK 21, lengkap dengan keluaran aslinya. Setelah membaca topik ini, sebuah skrip Groovy sederhana dapat dibaca, dijelaskan mekanismenya, dan dijalankan ulang dengan keluaran yang sama.
Bahasa dinamis di atas JVM#
Groovy adalah bahasa dinamis untuk JVM, yaitu bahasa yang memeriksa tipe dan keanggotaan metode saat program berjalan, bukan saat dikompilasi. Sintaksnya mengikuti Java sehingga terasa akrab bagi pengembang Java, tetapi penulisannya jauh lebih ringkas. Tipe tetap dapat dideklarasikan, tetapi tidak diwajibkan, karena variabel dapat ditulis dengan def dan tipenya mengikuti nilai yang diberikan. Yang paling penting dipahami, kode Groovy tetap dikompilasi menjadi bytecode yang disimpan dalam berkas .class, format yang sama dengan hasil kompilasi Java, sehingga tidak ada interpreter terpisah yang perlu dipasang.
Bukti perilakunya dapat dilihat dari dalam skrip itu sendiri. Setiap berkas skrip dikompilasi menjadi kelas turunan groovy.lang.Script, dan kelas hasil kompilasi itulah yang dieksekusi oleh JVM.
// Kode Groovy dikompilasi menjadi bytecode, bukan ditafsirkan per baris
println "Superclass skrip : ${this.class.superclass.name}"
println "Memuat metode run : ${this.class.methods*.name.contains('run')}"
println "Memuat metode main : ${this.class.methods*.name.contains('main')}"Superclass skrip : groovy.lang.Script
Memuat metode run : true
Memuat metode main : trueKeluaran tersebut memperlihatkan dua fakta. Superclass skrip adalah groovy.lang.Script, kelas milik Groovy yang menyediakan metode run() sebagai titik masuk eksekusi. Metode main() juga tersedia pada kelas hasil kompilasi, sehingga JVM menjalankan skrip Groovy sebagaimana menjalankan program Java. Karena hasil akhirnya berupa bytecode, satu berkas .groovy dapat dijalankan pada mesin apa pun yang memiliki JVM. Kompilasi lebih dulu (ahead-of-time) dengan groovyc juga dimungkinkan bila hasilnya ingin disebarkan sebagai berkas .class.
Interoperabilitas dengan Java#
Kelas-kelas JDK dan pustaka Java dipakai langsung dari Groovy tanpa lapisan jembatan. Penulisan import sama dengan Java, pemanggilan metodenya sama, dan classpath-nya juga sama, sehingga berkas .jar milik proyek Java dapat langsung dimanfaatkan oleh skrip Groovy. Classpath adalah daftar lokasi yang diperiksa JVM saat memuat kelas. Dua kelas JDK dipanggil pada contoh berikut, yaitu LocalDate untuk tanggal dan TreeMap untuk peta yang kuncinya selalu terurut, dan keduanya dipakai tanpa persiapan apa pun.
import java.time.LocalDate
import java.time.format.TextStyle
import java.util.Locale
import java.util.TreeMap
def tanggalContoh = LocalDate.of(2026, 8, 17)
println "Tanggal : $tanggalContoh"
println "Hari : ${tanggalContoh.dayOfWeek.getDisplayName(TextStyle.FULL, Locale.forLanguageTag('id'))}"
println "Tahun kabisat: ${tanggalContoh.isLeapYear()}"
def stok = new TreeMap<String, Integer>()
stok.put('buku', 24)
stok.put('amplop', 50)
stok.put('pena', 100)
println "Kunci terurut: ${stok.keySet()}"
println "Total satuan : ${stok.values().sum()}"Tanggal : 2026-08-17
Hari : Senin
Tahun kabisat: false
Kunci terurut: [amplop, buku, pena]
Total satuan : 174Tidak ada konversi tipe maupun adaptor yang ditulis. Objek yang dikembalikan adalah objek JDK yang sebenarnya, sehingga metode seperti getDisplayName() dan isLeapYear() tersedia lengkap beserta dokumentasinya. Nilai LocalDate.of(2026, 8, 17) pada contoh adalah data tetap yang dipilih agar keluarannya dapat direproduksi, bukan tanggal hari ini. Pola yang sama berlaku untuk pustaka pihak ketiga berformat .jar, yaitu cukup diletakkan di classpath lalu import-nya ditulis. Hubungan ini berlaku dua arah karena kelas Groovy juga dapat dipakai dari Java sebagai kelas bytecode biasa. Paket java.util bahkan termasuk impor bawaan Groovy, sehingga baris import pada contoh lebih merupakan kebiasaan yang baik daripada keharusan.
Keringkasan sintaks#
Keringkasan Groovy bersumber dari penghapusan boilerplate, yaitu kode formalitas yang wajib ditulis di Java, bukan dari perilaku yang berbeda. Berkas berikut menghitung statistik sederhana dari tiga nilai ujian. Deklarasi kelas publik, metode main, titik koma, dan penulisan tipe variabel semuanya dilewati, sedangkan daftar nilai ditulis sebagai literal yang langsung siap dipakai dan penyaringnya berupa blok kode { it >= 78 }.
// Skrip Groovy: tanpa kelas, tanpa metode main, tanpa titik koma
def nilai = [80, 75, 85]
def lulus = nilai.findAll { it >= 78 }
println "Nilai : $nilai"
println "Lulus : $lulus"
println "Terendah : ${nilai.min()}"
println "Terbesar : ${nilai.max()}"
println "Total : ${nilai.sum()}"Nilai : [80, 75, 85]
Lulus : [80, 85]
Terendah : 75
Terbesar : 85
Total : 240Berkas itu disimpan sebagai ringkas.groovy dan dijalankan dari direktori tempat berkasnya berada.
# tidak-dijalankan
# menjalankan skrip Groovy dari direktori tempat berkasnya berada
groovy ringkas.groovyMetode findAll, min, max, dan sum pada daftar bukan milik java.util.List di JDK. Metode-metode itu adalah tambahan pustaka standar Groovy, yaitu GDK (Groovy Development Kit), yang ditempelkan pada kelas-kelas JDK. Tambahan semacam ini ikut mewarisi interoperabilitas dengan Java, karena tetap bekerja pada objek JDK yang sama.
Skrip yang setara ditulis dalam Java memerlukan pembungkus kelas publik, metode main dengan parameter String[] args, penulisan tipe secara eksplisit, dan pemanggilan System.out.println untuk setiap baris keluaran.
// tidak-dijalankan
import java.util.List;
public class Ringkas {
public static void main(String[] args) {
List<Integer> nilai = List.of(80, 75, 85);
List<Integer> lulus = nilai.stream().filter(n -> n >= 78).toList();
System.out.println("Nilai : " + nilai);
System.out.println("Lulus : " + lulus);
System.out.println("Terendah : " + java.util.Collections.min(nilai));
System.out.println("Terbesar : " + java.util.Collections.max(nilai));
System.out.println("Total : " + nilai.stream().mapToInt(Integer::intValue).sum());
}
}Kedua versi menghasilkan keluaran yang persis sama, sedangkan jumlah baris dan penulisan yang diperlukan berbeda jauh.
Posisi Groovy di ekosistem JVM#
Groovy sering dibaca sebelum ditulis. Alat build Gradle mengeksekusi berkas build berbahasa Groovy, Jenkins menjalankan pipeline yang instruksinya ditulis dalam Groovy, dan kerangka pengujian Spock memakai Groovy sebagai bahasa spesifikasinya. Akibatnya, sebagian insinyur Java pertama kali berjumpa dengan Groovy saat menyunting berkas build.gradle atau langkah pipeline yang ditulis orang lain, bukan saat membuat skrip baru. Contohnya mudah ditemukan, karena berkas build.gradle milik proyek Java dan deklarasi pipeline pada Jenkins keduanya adalah teks Groovy yang dapat dibaca tanpa alat tambahan. Kemampuan membaca Groovy dengan demikian menjadi kebutuhan praktis, dan pemahaman tentang sintaks dasarnya sudah cukup untuk memenuhinya.
Dalam sejarahnya, Groovy dirancang oleh James Strachan dan Bob McWhirter serta pertama kali diperkenalkan pada 2003. Proyek ini diterima ke inkubator Apache Software Foundation pada 2015, lalu lulus sebagai proyek top-level pada November 2015. Seluruh kode sumbernya dirilis di bawah lisensi Apache 2.0, yang mengizinkan pemakaian, modifikasi, dan distribusi ulang secara bebas, termasuk di dalam produk komersial.
Versi dan jalur dukungan#
Tiga lini versi sedang berlaku pada saat topik ini ditulis. Lini 5.1 adalah versi stabil terbaru yang dirancang untuk JDK 11 ke atas, dengan 5.1.3 sebagai rilis terkininya. Lini 6.0 dirilis final pada 22 September 2026 dan dirancang untuk JDK 17 ke atas. Lini 4.0 masih dipelihara untuk lingkungan yang terikat pada JDK 8. Aturan pemilihannya sederhana, yaitu lini versi mengikuti versi JDK yang tersedia, bukan sebaliknya. Halaman unduhan resmi Groovy mencantumkan syarat JVM untuk setiap lini, sehingga penentuannya dapat diperiksa ulang.
Nomor versinya mengikuti skema semantik, yaitu angka pertama bertambah pada perubahan yang menimbulkan ketidakcocokan, sehingga loncatan dari 5.1 ke 6.0 sebaiknya disertai pembacaan catatan rilis.
Dinamis dan statis adalah dua produk berbeda#
Satu hal yang sering disalahpahami, skrip dinamis dan pustaka terkompilasi statis adalah dua produk yang berbeda, meskipun keduanya ditulis dengan sintaks Groovy yang sama. Skrip dinamis ditulis untuk otomasi sekali jalan, eksperimen cepat, dan kode perekat antar pustaka (glue code), dan seluruh pemeriksaan tipenya berlangsung saat program berjalan. Pustaka yang akan dipakai ulang oleh banyak pihak umumnya dikompilasi statis dengan anotasi seperti @CompileStatic, sehingga pemeriksaan tipe berpindah ke waktu kompilasi dan error tertangkap sebelum kode pernah dijalankan. Keduanya berdampingan dalam satu bahasa, dan anotasi seperti @TypeChecked mengatur seberapa ketat pemeriksaan tipe dilakukan. Perbedaan itu terasa pada cara memverifikasi keduanya, yaitu skrip dinamis dijalankan untuk diuji, sedangkan pustaka statis diperiksa oleh kompilator pada setiap kompilasi.
Kesalahan umum#
Kesalahan pertama adalah menganggap penulisan tipe eksplisit memberi jaminan pemeriksaan saat kompilasi. Pada skrip dinamis, deklarasi tipe tetap dievaluasi saat program berjalan. Konversi yang memungkinkan dilakukan secara diam-diam, sedangkan konversi yang mustahil ditolak dengan error saat runtime.
String nama = 42
println "nama : $nama (${nama.getClass().simpleName})"
try {
int angka = 'tujuh'
} catch (Exception e) {
println "angka: gagal saat runtime, ${e.getClass().simpleName}"
}nama : 42 (String)
angka: gagal saat runtime, GroovyCastExceptionPenugasan String nama = 42 diterima karena angka 42 dikonversi diam-diam menjadi teks "42". Penugasan int angka = 'tujuh' lolos kompilasi, tetapi gagal saat runtime dengan GroovyCastException karena teks tersebut tidak dapat dikonversi menjadi bilangan bulat. Pada kode terkompilasi statis, kasus kedua sudah tertangkap saat kompilasi.
Kesalahan kedua adalah salah mengeja nama metode. Pada skrip dinamis, ejaan tidak diperiksa saat kompilasi, sehingga error baru muncul pada baris yang dieksekusi.
def teks = 'catatan'
try {
teks.balikKata()
} catch (MissingMethodException e) {
println "Gagal saat runtime: ${e.getClass().simpleName}"
}Gagal saat runtime: MissingMethodExceptionRuntime Groovy mencari metode balikKata() pada objek String, tidak menemukannya, lalu melempar MissingMethodException. Perbaikannya ada dua, yaitu memeriksa ejaan pada dokumentasi GDK atau mengaktifkan pemeriksaan statis untuk bagian kode yang menuntut jaminan kompilasi. Kesalahan semacam ini jarang lolos pada kode yang diuji, tetapi mudah tersembunyi pada skrip panjang yang sebagian jalurnya jarang dijalankan.
Ringkasan#
- Groovy adalah bahasa dinamis untuk JVM yang dikompilasi menjadi bytecode, sehingga skripnya dijalankan oleh JVM biasa tanpa interpreter terpisah.
- Kelas JDK dan pustaka Java dipakai langsung dari Groovy melalui
importdan classpath yang sama, dan kelas Groovy juga dapat dipakai dari Java. - Keringkasan sintaksnya berasal dari penghapusan boilerplate Java, sedangkan GDK menambahkan metode praktis seperti
findAlldansumpada kelas-kelas JDK. - Lini 5.1 stabil untuk JDK 11 ke atas, 6.0 final untuk JDK 17 ke atas, dan 4.0 dipelihara untuk JDK 8, sedangkan skrip dinamis dan pustaka terkompilasi statis tetap dua produk yang berbeda.
Referensi#
- The Apache Groovy programming language — Apache Groovy Project — halaman resmi bahasa yang memuat deskripsi sifatnya, yaitu pengetikan statis dan dinamis sekaligus dukungan skrip.
- Download Groovy — Apache Groovy Project — daftar lini versi resmi beserta syarat JVM, termasuk 5.1 untuk JDK 11+ dan 4.0 untuk JDK 8+.
- Groovy 5.1 documentation — Apache Groovy Project — dokumentasi referensi resmi Groovy 5.1 yang menjadi dasar perilaku skrip dan kompilasinya.
- Apache Groovy — Wikipedia — ringkasan sejarah bahasa dari perkenalan tahun 2003 hingga kelulusannya dari inkubator Apache pada 2015.