Dua artikel internet bisa saling bertentangan soal perilaku satu baris kode — misalnya apa tipe hasil byte + byte. Untuk pertanyaan semacam itu, satu dokumen berkedudukan sebagai hakim terakhir: Java Language Specification (JLS). JLS adalah kontrak normatif bahasa Java yang ditulis dan dirawat perancang bahasanya, bukan tutorial ramah pembaca. Chapter ini melatih cara membacanya secara terarah: menemukan pasal yang relevan, memahami struktur dokumen, lalu membuktikan setiap klaim lewat kode kecil.
Apa Itu Java Language Specification #
JLS mendefinisikan struktur dan makna program Java: tata bahasa, konversi tipe, evaluasi ekspresi, sampai aturan eksekusi. Dokumen ini diterbitkan ulang tiap rilis JDK besar; edisi Course ini adalah JLS SE25 di docs.oracle.com/javase/specs/jls/se25/html/index.html, sementara indeks seluruh edisi beserta PDF-nya berada di docs.oracle.com/javase/specs/. Isinya terbagi menjadi 19 bab; yang paling sering kita buka antara lain bab 5 Conversions and Contexts, bab 15 Expressions, dan bab 16 Definite Assignment. Sifatnya normatif: bila compiler dan JLS berbeda pendapat, compiler yang wajib menyesuaikan.
Anatomi Pasal dan Cara Menelusuri #
Setiap aturan di JLS diberi nomor pasal bertingkat yang sekaligus menjadi jangkar alamat — sitasi §5.6 menunjuk pasal 6 dari bab 5 dan bisa ditautkan langsung di versi HTML. Cara menelusur yang efisien: buka index.html untuk melihat daftar 19 bab, buka berkas bab yang relevan (jls-5.html dan seterusnya), lalu tekan Ctrl-F dengan nomor pasal. Di dalam pasal, kotak grammar menampilkan produksi tata bahasa, contoh program diberi label bernomor seperti Example 5.0-1, dan paragraf Discussion menjelaskan motivasi di balik aturan.
Kasus Pertama: Promosi Numerik #
Pertanyaan klasik pemula: mengapa byte c = a + b; ditolak compiler javac padahal hasilnya muat di byte? Jawabannya ada di pasal 5.6 Numeric Contexts: sebelum operator biner dievaluasi, operand numerik yang lebih kecil dari int dipromosikan lebih dulu, sehingga byte + byte menghasilkan int. Compiler javac menolaknya dengan pesan incompatible types: possible lossy conversion from int to byte. Program berikut memperlihatkan aturan yang sama dari sisi yang lolos kompilasi:
public class PromosiNumerik {
public static void main(String[] args) {
byte a = 100;
byte b = 28;
int jumlah = a + b; // byte + byte dipromosikan ke int (JLS 5.6)
System.out.println("a + b : " + jumlah);
byte padat = (byte) (a + b); // pengecoran eksplisit: 128 meluap menjadi -128
System.out.println("(byte) (a + b) : " + padat);
char huruf = 'A';
int kode = huruf + 1; // char + int juga dipromosikan ke int (JLS 5.6)
System.out.println("'A' + 1 : " + kode);
huruf += 1; // padanan (char) (huruf + 1), pengecoran implisit (JLS 15.26.2)
System.out.println("huruf += 1 : " + huruf);
boolean lulus = true;
double nilai = lulus ? 1 : 2.5; // ekspresi kondisional: 1 dipromosikan ke double (JLS 15.25)
System.out.println("true ? 1 : 2.5 : " + nilai);
}
}Output yang Dihasilkan:
a + b : 128
(byte) (a + b) : -128
'A' + 1 : 66
huruf += 1 : B
true ? 1 : 2.5 : 1.0Mari kita telaah aturan yang bekerja di baliknya:
- Baris 7 (
int jumlah = a + b;): kedua operandbytedipromosikan keintsebelum dijumlahkan, hasilnya 128, dan penugasan keintsah. - Baris 10 (
(byte) (a + b)): pengecoran eksplisit memaksa 128 kembali kebyte; rentangbytehanya -128 sampai 127 sehingga nilai meluap menjadi -128. - Baris 14 dan 17 (
huruf + 1laluhuruf += 1):char + intdipromosikan keintsehingga hasilnya 66;+=adalah operator penugasan majemuk yang oleh pasal 15.26.2 diizinkan menyertakan pengecoran implisit, jadihuruftetapcharberisiB. - Baris 21 (
lulus ? 1 : 2.5): pasal 15.25 menetapkan tipe ekspresi kondisional dari kedua cabangnya; karena satu cabang bertipedouble, literal1dipromosikan dan hasilnya1.0.
Kasus Kedua: Definite Assignment #
Pertanyaan kedua yang sering muncul: mengapa variabel lokal yang diisi di dalam blok if ditolak saat dipakai setelahnya? Pasal 16 Definite Assignment mewajibkan setiap variabel lokal pasti terisi sebelum nilainya dibaca — compiler javac menganalisis seluruh jalur eksekusi, bukan hanya jalur yang sering diambil. Berikut contoh yang sengaja dibiarkan gagal dikompilasi:
public class KuotaBelumPasti {
public static void main(String[] args) {
int kuota;
if (args.length > 0) {
kuota = Integer.parseInt(args[0]);
}
// Pada jalur tanpa argumen, kuota belum pasti terisi (JLS 16).
System.out.println("Kuota awal: " + kuota);
}
}Eksekusi lewat java KuotaBelumPasti.java berhenti di tahap kompilasi dengan pesan persis: error: variable kuota might not have been initialized. Variabel kuota hanya terisi ketika program menerima argumen; pada jalur tanpa argumen, compiler tidak bisa membuktikan kuota pasti terisi sehingga pemakaiannya ditolak. Perbaikannya sederhana: beri nilai awal saat deklarasi (int kuota = 0;) atau pastikan semua cabang mengisi variabel itu.
JLS Bukan Satu-satunya Spesifikasi #
JLS mengawasi bahasa, tetapi tiga dokumen tetangga melengkapi peta rujukan. JVM Specification di docs.oracle.com/javase/specs/jvms/se25/html/ menjelaskan eksekusi bytecode: struktur berkas class, verifikasi, dan instruksi. Perilaku library bawaan seperti String atau Collections dicatat di Javadoc, bukan di JLS. Motivasi fitur baru didokumentasikan sebagai JEP di openjdk.org/jeps/0.
Membaca JLS memang menuntut kebiasaan — kalimatnya padat dan penuh istilah teknis. Imbalannya pasti: setiap perdebatan soal perilaku bahasa bisa diselesaikan dengan sitasi pasal, bukan dengan pendapat. Setelah bahasa, sumber yang paling sering kita buka adalah dokumentasi API; chapter berikutnya melatih cara membaca Javadoc secara efisien.
Referensi #
- JLS SE25 — halaman utama
- JLS SE25 — Bab 5, Conversions and Contexts
- JLS SE25 — Bab 16, Definite Assignment
- JVM Specification SE25
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
KuotaBelumPasti.java
Unduh berkaspublic class KuotaBelumPasti {
public static void main(String[] args) {
int kuota;
if (args.length > 0) {
kuota = Integer.parseInt(args[0]);
}
// Pada jalur tanpa argumen, kuota belum pasti terisi (JLS 16).
System.out.println("Kuota awal: " + kuota);
}
}
PromosiNumerik.java
Unduh berkaspublic class PromosiNumerik {
public static void main(String[] args) {
byte a = 100;
byte b = 28;
int jumlah = a + b; // byte + byte dipromosikan ke int (JLS 5.6)
System.out.println("a + b : " + jumlah);
byte padat = (byte) (a + b); // pengecoran eksplisit: 128 meluap menjadi -128
System.out.println("(byte) (a + b) : " + padat);
char huruf = 'A';
int kode = huruf + 1; // char + int juga dipromosikan ke int (JLS 5.6)
System.out.println("'A' + 1 : " + kode);
huruf += 1; // padanan (char) (huruf + 1), pengecoran implisit (JLS 15.26.2)
System.out.println("huruf += 1 : " + huruf);
boolean lulus = true;
double nilai = lulus ? 1 : 2.5; // ekspresi kondisional: 1 dipromosikan ke double (JLS 15.25)
System.out.println("true ? 1 : 2.5 : " + nilai);
}
}