Kode Java klasik sering mengulang informasi yang sama dua kali: Map<String, List<String>> data = new HashMap<String, List<String>>() menuliskan tipe generik identik di kiri dan kanan tanda sama dengan. Deklarasi ganda semacam itu tidak menambah informasi apa pun bagi pembaca, tetapi menambah kebisingan yang nyata pada berkas panjang. Fitur var, difinalkan di Java 10 melalui JEP 286 lalu diperluas ke parameter lambda di Java 11 melalui JEP 323, mengizinkan compiler menyimpulkan tipe variabel lokal dari ekspresi inisialisasinya. Chapter ini membedah cara kerja penyimpulan tersebut, batas-batasnya, dan selera pemakaian yang menjaga kode tetap terbaca.
Cara Kerja var di Balik Layar #
var bukan kata kunci melainkan reserved type name: hanya diperlakukan khusus ketika menempati posisi tipe. Ketika compiler javac membaca var harga = 15_000;, compiler melihat ekspresi di sisi kanan bertipe int, lalu mengganti var menjadi int pada saat kompilasi. Hasilnya persis sama dengan menulis int harga = 15_000; — bytecode identik, dan var tidak mengubah Java menjadi bahasa bertipe dinamis. Konsekuensinya tegas: variabel var tetap strongly typed, tidak bisa berganti tipe di tengah jalan, dan kesalahan tipe tetap tertangkap saat kompilasi.
Deklarasi Ringkas dalam Praktik #
Program berikut membandingkan deklarasi klasik dengan var pada struktur data yang sama, lalu membuktikan keduanya menghasilkan objek dengan tipe dan isi identik:
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class GayaDeklarasi {
public static void main(String[] args) {
// Deklarasi klasik: tipe generik yang sama ditulis di kedua sisi '='.
Map<String, List<String>> belanjaKlasik = new TreeMap<>(Map.of(
"toko Buah", List.of("jeruk", "apel", "pisang"),
"toko Roti", List.of("roti gandum", "croissant")));
// Deklarasi var: compiler menyimpulkan tipe dari sisi kanan.
var belanja = new TreeMap<>(Map.of(
"toko Buah", List.of("jeruk", "apel", "pisang"),
"toko Roti", List.of("roti gandum", "croissant")));
System.out.println("isi sama : " + belanjaKlasik.equals(belanja));
System.out.println("kelas var : " + belanja.getClass().getSimpleName());
for (var entri : belanja.entrySet()) {
System.out.println(entri.getKey() + " -> " + entri.getValue().size() + " item");
}
var hargaSatuan = 15_000;
var jumlah = 3;
var total = hargaSatuan * (long) jumlah;
System.out.println("total : Rp" + total);
}
}Output yang Dihasilkan:
isi sama : true
kelas var : TreeMap
toko Buah -> 3 item
toko Roti -> 2 item
total : Rp45000Bedah baris-per-baris untuk bagian pentingnya:
- Baris
Map<String, List<String>> belanjaKlasik = new TreeMap<>(...): gaya klasik — tipe deklarasiMapditulis manual meskipun sisi kanan sudah menyatakannya secara implisit. - Baris
var belanja = new TreeMap<>(...): compiler menyimpulkanTreeMap<String, List<String>>; pembaca pun tahu persis tipe konkret dari baris itu tanpa menebak. - Baris
System.out.println("kelas var : " + belanja.getClass().getSimpleName()): mencetakTreeMap— buktivarmemilih tipe konkret sisi kanan, bukan tipe abstrak mana pun. - Baris
for (var entri : belanja.entrySet()):varjuga sah pada pernyataanforenhanced;entridisimpulkan sebagaiMap.Entry<String, List<String>>.
Kapan var Membantu dan Kapan Mengganggu #
var paling membantu ketika tipe sudah terlihat jelas di sisi kanan: ekspresi new, hasil factory seperti List.of, atau panggilan method yang namanya deskriptif. Sebaliknya, var mengganggu ketika tipe konkret tidak terlihat dari baris deklarasi — pembaca terpaksa melompat ke definisi method lain hanya untuk mengetahui tipe.
| Situasi | Contoh | Rekomendasi |
|---|---|---|
| Tipe terlihat di sisi kanan | var saldo = new BigDecimal("50000") | Gunakan var |
| Nilai dari factory jelas | var nama = List.of("Budi", "Siti") | Gunakan var |
| Tipe tidak terlihat | var data = layanan.muat() | Tulis tipe eksplisit |
| Nilai literal sederhana | var jumlah = 3 | Boleh keduanya, konsisten saja |
Batas Pemakaian var #
var hanya berlaku untuk variabel lokal di dalam method, constructor, atau blok inisialisasi — bukan untuk field class, parameter method, atau tipe kembalian. Dua syarat tambahannya: deklarasi harus punya inisialisasi (var x; ditolak dengan pesan cannot use ‘var’ on variable without initializer), dan inisialisator harus punya tipe yang dapat disimpulkan. Lambda berdiri sendiri gagal pada syarat kedua, seperti contoh gagal berikut:
public class VarLambda {
public static void main(String[] args) {
// var tidak dapat menyimpulkan tipe lambda yang berdiri sendiri.
var pengganda = () -> 5;
System.out.println(pengganda);
}
}Compiler javac menolak berkas di atas dengan pesan cannot infer type for local variable pengganda (lambda expression needs an explicit target-type) — sebuah lambda memang tidak memiliki tipe sendiri sebelum dipasangkan pada tipe fungsional seperti java.util.function.IntSupplier. Solusinya: tulis tipe fungsional secara eksplisit, atau berikan lambda langsung sebagai argumen method yang menentukan parameternya.
Deklarasi ringkas hanyalah lapisan pertama modernisasi Java. Struktur kontrolnya juga berubah: switch lama yang gemar bocor karena lupa break digantikan bentuk expression yang aman dan mengembalikan nilai.
Referensi #
- OpenJDK: JEP 323 — Local-Variable Syntax for Lambda Parameters
- OpenJDK: JEP 286 — Local-Variable Type Inference
- dev.java — Variable Type Inference (var)
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
GayaDeklarasi.java
Unduh berkasimport java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class GayaDeklarasi {
public static void main(String[] args) {
// Deklarasi klasik: tipe generik yang sama ditulis di kedua sisi '='.
Map<String, List<String>> belanjaKlasik = new TreeMap<>(Map.of(
"toko Buah", List.of("jeruk", "apel", "pisang"),
"toko Roti", List.of("roti gandum", "croissant")));
// Deklarasi var: compiler menyimpulkan tipe dari sisi kanan.
var belanja = new TreeMap<>(Map.of(
"toko Buah", List.of("jeruk", "apel", "pisang"),
"toko Roti", List.of("roti gandum", "croissant")));
System.out.println("isi sama : " + belanjaKlasik.equals(belanja));
System.out.println("kelas var : " + belanja.getClass().getSimpleName());
for (var entri : belanja.entrySet()) {
System.out.println(entri.getKey() + " -> " + entri.getValue().size() + " item");
}
var hargaSatuan = 15_000;
var jumlah = 3;
var total = hargaSatuan * (long) jumlah;
System.out.println("total : Rp" + total);
}
}
VarLambda.java
Unduh berkaspublic class VarLambda {
public static void main(String[] args) {
// var tidak dapat menyimpulkan tipe lambda yang berdiri sendiri.
var pengganda = () -> 5;
System.out.println(pengganda);
}
}