Utilitas HitungKata pada chapter sebelumnya cukup dengan satu argumen posisi, tetapi utilitas sungguhan jarang sesederhana itu. Program nyata mengenal flag tanpa nilai (-v), opsi dengan nilai (-o laporan.csv), bentuk pendek dan panjang (-v dan --verbose), argumen posisi (SUMBER...), penanda -- yang mengakhiri wilayah opsi, dan beragam bentuk kesalahan yang harus ditolak dengan pesan yang jelas. Array args pada main datang apa adanya — setiap elemen adalah string utuh tanpa tanda hubung yang ditafsirkan — sehingga seluruh pekerjaan penguraian ada di tangan kita. Chapter ini membangun parser manual yang tertib, lalu melihat kapan waktunya beralih ke library parsing.
Anatomi Argumen Command Line #
Empat jenis token mengisi array args, dan parser yang baik memperlakukan keduanya dengan aturan berbeda:
| Jenis | Contoh | Perlakuan |
|---|---|---|
| Flag | -v, --verbose | nilainya implisit: aktif |
| Opsi bernilai | -o laporan.csv, --format json | nilai ada di argumen berikutnya |
| Posisi | data-a, sumber.txt | makna bergantung urutan kemunculan |
| Penanda akhir | -- | semua argumen setelahnya dianggap posisi |
Penanda -- sering dilupakan padahal menyelamatkan nama berkas yang diawali tanda hubung: java EksporData.java -- -v mengartikan -v sebagai nama sumber, bukan flag verbose.
Parser Manual yang Tertib #
Program berikut menguraikan seluruh jenis token itu dengan satu loop dan switch — bentuk yang mudah diperiksa baris demi baris. Setiap pelanggaran (opsi tak dikenal, nilai hilang, nilai tidak sah) berujung pada satu method gagal yang mencetak pemakaian dan keluar dengan kode 2, konsisten dengan konvensi galat pemakaian:
import java.util.ArrayList;
import java.util.List;
public class EksporData {
public static void main(String[] args) {
boolean verbose = false;
String berkasKeluaran = null;
String format = "csv";
List<String> sumber = new ArrayList<>();
boolean opsiSelesai = false; // true setelah bertemu "--"
int i = 0;
while (i < args.length) {
String a = args[i];
if (opsiSelesai) { // sisanya positional, apa pun bentuknya
sumber.add(a);
i++;
continue;
}
switch (a) {
case "-v", "--verbose" -> verbose = true;
case "-o", "--output" -> {
if (i + 1 >= args.length) {
gagal("opsi " + a + " memerlukan nama berkas");
}
berkasKeluaran = args[++i];
}
case "--format" -> {
if (i + 1 >= args.length
|| !List.of("csv", "json").contains(args[i + 1])) {
gagal("opsi --format memerlukan nilai csv atau json");
}
format = args[++i];
}
case "--" -> opsiSelesai = true;
default -> {
if (a.startsWith("-")) {
gagal("opsi tidak dikenal: " + a);
}
sumber.add(a);
}
}
i++;
}
System.out.println("verbose : " + verbose);
System.out.println("keluaran: " + berkasKeluaran);
System.out.println("format : " + format);
System.out.println("sumber : " + sumber);
}
private static void gagal(String pesan) {
System.err.println("Galat: " + pesan);
System.err.println("Penggunaan: java EksporData.java "
+ "[-v] [-o BERKAS] [--format csv|json] SUMBER...");
System.exit(2);
}
}Output yang Dihasilkan:
verbose : true
keluaran: laporan.csv
format : csv
sumber : [data-a, data-b]Mari kita telaah struktur loop-nya:
- Baris
if (opsiSelesai): begitu--ditemukan, seluruh argumen sisanya masuk daftar sumber tanpa pemeriksaan — tanda hubung pun diterima sebagai nama. - Baris
case "-v", "--verbose": multi-labelcasemenyatukan bentuk pendek dan panjang dalam satu cabang — satu aturan, dua ejaan. - Baris
berkasKeluaran = args[++i]:++imelompati argumen nilai sekaligus mengonsumsinya, sehingga nilai tidak diproses ulang sebagai token opsi. - Baris
!List.of("csv", "json").contains(args[i + 1]): validasi nilai dilakukan saat parsing, bukan belakangan saat dipakai — galat melapor dekat sumbernya. - Method
gagal: satu pintu keluar untuk semua pelanggaran pemakaian; pesan galat dan baris pemakaian selalu tampil berpasangan, dan exit code2konsisten dengan konvensi GNU.
Variasinya bekerja seperti dugaan — opsi panjang dengan nilai, lalu -- yang mengubah makna -v:
$ java EksporData.java --format json sumber.txt
verbose : false
keluaran: null
format : json
sumber : [sumber.txt]
$ java EksporData.java -- -v
verbose : false
keluaran: null
format : csv
sumber : [-v]
$ java EksporData.java -x
Galat: opsi tidak dikenal: -x
Penggunaan: java EksporData.java [-v] [-o BERKAS] [--format csv|json] SUMBER...Kapan Beralih ke Library Parsing #
Parser manual menjadi mahal ketika kebutuhan tumbuh: opsi bersarang, nilai default yang terdokumentasi, pembangkitan --help otomatis, dan penyelesaian nama berkas. Dua library lazim mengisi celah itu — picocli dengan anotasi deklaratif, dan Apache Commons CLI dengan builder berantai. Keduanya berada di luar JDK, sehingga contoh berikut tampil sebagai gambaran bentuk, bukan blok yang dieksekusi di buku ini:
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import java.util.List;
@Command(name = "ekspor", mixinStandardHelpOptions = true,
description = "Mengekspor data ke format csv atau json.")
public class Ekspor implements Runnable {
@Option(names = {"-v", "--verbose"}, description = "tampilkan progres detail")
boolean verbose;
@Option(names = {"-o", "--output"}, paramLabel = "BERKAS",
description = "berkas keluaran")
String berkas;
@Option(names = "--format", defaultValue = "csv",
description = "csv atau json")
String format;
@Parameters(paramLabel = "SUMBER", description = "sumber data")
List<String> sumber;
@Override
public void run() { /* ... */ }
public static void main(String[] args) {
System.exit(new CommandLine(new Ekspor()).execute(args));
}
}<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>4.7.7</version>
</dependency>| Pertimbangan | Parser manual | Library (picocli / Commons CLI) |
|---|---|---|
| Ketergantungan | tidak ada | satu artefak eksternal |
--help otomatis | ditulis tangan | dibangkitkan dari deklarasi |
| Opsi kompleks | dikode sendiri | siap pakai |
| Cocok untuk | utilitas < ±10 opsi | aplikasi dengan banyak opsi |
Dengan masukan terurai, komponen berikutnya dari otomasi adalah memproses data dalam volume besar secara bertahap — tanpa mengulang dari awal ketika proses terputus di tengah jalan.
Referensi #
- Oracle: Alat java — Opsi dan Argumen
- OpenJDK: JEP 458 — Launch Multi-File Source Programs
- Apache Maven: Panduan Dependensi
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
EksporData.java
Unduh berkasimport java.util.ArrayList;
import java.util.List;
public class EksporData {
public static void main(String[] args) {
boolean verbose = false;
String berkasKeluaran = null;
String format = "csv";
List<String> sumber = new ArrayList<>();
boolean opsiSelesai = false; // true setelah bertemu "--"
int i = 0;
while (i < args.length) {
String a = args[i];
if (opsiSelesai) { // sisanya positional, apa pun bentuknya
sumber.add(a);
i++;
continue;
}
switch (a) {
case "-v", "--verbose" -> verbose = true;
case "-o", "--output" -> {
if (i + 1 >= args.length) {
gagal("opsi " + a + " memerlukan nama berkas");
}
berkasKeluaran = args[++i];
}
case "--format" -> {
if (i + 1 >= args.length
|| !List.of("csv", "json").contains(args[i + 1])) {
gagal("opsi --format memerlukan nilai csv atau json");
}
format = args[++i];
}
case "--" -> opsiSelesai = true;
default -> {
if (a.startsWith("-")) {
gagal("opsi tidak dikenal: " + a);
}
sumber.add(a);
}
}
i++;
}
System.out.println("verbose : " + verbose);
System.out.println("keluaran: " + berkasKeluaran);
System.out.println("format : " + format);
System.out.println("sumber : " + sumber);
}
private static void gagal(String pesan) {
System.err.println("Galat: " + pesan);
System.err.println("Penggunaan: java EksporData.java "
+ "[-v] [-o BERKAS] [--format csv|json] SUMBER...");
System.exit(2);
}
}