Setiap variabel di Java membawa dua sifat sekaligus: lingkup, yaitu wilayah kode yang masih bisa membaca nama variabel tersebut, dan masa hidup, yaitu lamanya nilai tersimpan. Keduanya ditentukan oleh tempat deklarasi ditulis, bukan oleh keinginan pemrogram di tengah kode. Topik ini memetakan lingkup parameter, variabel lokal, blok, dan field, lalu membongkar jebakan shadowing yang membuat dua nama tampak seperti satu.
Peta lingkup variabel#
Jenis variabel yang relevan pada tahap ini ada lima, dan masing-masing membawa lingkup serta masa hidup yang berbeda. Perbedaannya meringkas menjadi satu kaidah: makin dalam deklarasi ditulis, makin sempit wilayahnya dan makin singkat umurnya.
| Jenis variabel | Tempat deklarasi | Lingkupnya | Masa hidupnya |
|---|---|---|---|
| parameter | daftar parameter metode | seluruh tubuh metode | selama pemanggilan metode berjalan |
| lokal | di dalam tubuh metode | dari baris deklarasinya sampai akhir blok yang melingkupinya | sama dengan lingkupnya |
| lokal blok | di dalam blok seperti if atau for | hanya blok itu | selama blok berjalan |
| field static | tingkat kelas dengan static | seluruh kelas | sepanjang program berjalan |
| field objek | tingkat kelas tanpa static | seluruh kelas | selama objeknya masih dipakai |
Gambar di atas menumpuk wilayah-wilayah itu dalam satu kelas. Field sapaan terbaca dari mana pun di dalam kelasnya, metode sapa menampung parameter nama dan lokal kalimat di wilayahnya sendiri, sedangkan i hanya hidup di blok for. Kaidah umumnya berlaku dua arah yang tidak simetris: kode pada lingkup luar tidak bisa melihat variabel lingkup dalam, sedangkan lingkup dalam bisa membaca semuanya yang ada di luarnya.
Lingkup lokal dan blok#
Variabel lokal mulai hidup pada baris deklarasinya dan mati di akhir blok yang melingkupinya. Blok adalah wilayah antara sepasang kurung kurawal, termasuk tubuh metode, tubuh if, dan tubuh for. Variabel perulangan i pada for adalah contohnya yang paling sering ditemui, karena ia hidup selama perulangan berjalan dan lenyap setelah bloknya selesai.
public class DemoLingkup {
static String sapaan = "Halo"; // field, milik kelas
static void sapa(String nama) { // parameter nama, hidup seluruh isi metode
String kalimat = sapaan + ", " + nama;
for (int i = 1; i <= 2; i++) { // i hanya hidup di dalam for
kalimat = kalimat + "!";
}
System.out.println(kalimat);
// System.out.println(i); // ditolak kompilator: i tidak dikenal di sini
}
static double hargaSetelahDiskon(double harga) {
double potongan = 0;
if (harga > 100000) {
double rasio = 0.1; // rasio hanya hidup di dalam blok if
potongan = harga * rasio;
}
return harga - potongan;
}
public static void main(String[] args) {
sapa("Dewi");
System.out.println(hargaSetelahDiskon(150000));
System.out.println(hargaSetelahDiskon(80000));
}
}Halo, Dewi!!
135000.0
80000.0Baris yang dikomentari pada program di atas akan ditolak kompilator bila diaktifkan, karena i sudah berada di luar wilayahnya. Hal yang sama menimpa rasio, yang dideklarasikan di dalam blok if dan tidak dikenal lagi pada baris return. Satu aturan lagi layak diingat: dua variabel lokal bernama sama tidak boleh hidup pada lingkup yang tumpang-tindih. Dua perulangan berbeda boleh masing-masing memakai i karena wilayahnya berdampingan, bukan menumpuk.
Bila nama bertabrakan, shadowing#
Shadowing terjadi saat lingkup dalam mendeklarasikan nama yang sudah dipakai di lingkup luarnya. Nama yang baru menutupi yang lama selama lingkup dalamnya berjalan, sehingga satu nama bisa merujuk dua variabel berbeda tergantung letak barisnya. Java melarang lokal menutupi lokal, tetapi membiarkan parameter menutupi field, dan kombinasi kedua itulah yang paling sering membingungkan.
public class DemoShadowing {
static int stok = 40; // field tingkat kelas
static void catatPengambilan(int stok) { // parameter menutupi field
System.out.println("Parameter stok: " + stok);
System.out.println("Field stok: " + DemoShadowing.stok);
stok = stok - 5; // mengubah parameter, bukan field
}
public static void main(String[] args) {
catatPengambilan(12);
System.out.println("Field setelah metode: " + stok);
}
}Parameter stok: 12
Field stok: 40
Field setelah metode: 40Parameter stok menutupi field stok di seluruh tubuh metode, sehingga pengurangan stok - 5 menyasar parameter dan field tidak tersentuh. Bila field yang memang dimaksud, rujuk secara eksplisit lewat nama kelas untuk field static, yaitu DemoShadowing.stok, atau lewat this untuk field objek. Cara lain yang lebih tahan salah adalah mengganti nama parameternya, misalnya menjadi jumlahDiambil, agar tidak ada penutupan nama sama sekali.
Masa hidup dan mengapa penting#
Masa hidup menjawab pertanyaan yang tidak dijawab lingkup, yaitu berapa lama nilai bertahan. Parameter dan variabel lokal hidup selama pemanggilan metode berjalan: Java menyimpannya pada frame metode di stack, dan frame itu dibuang begitu metode selesai. Field static bertahan sepanjang program berjalan, sedangkan field objek bertahan selama objeknya masih dipakai.
public class PenghitungPanggilan {
static int jumlahPanggilan = 0; // hidup sepanjang program berjalan
static void proses(String tugas) {
jumlahPanggilan++;
System.out.println(jumlahPanggilan + ". " + tugas);
}
public static void main(String[] args) {
proses("baca data");
proses("hitung ulang");
proses("cetak laporan");
}
}1. baca data
2. hitung ulang
3. cetak laporanField jumlahPanggilan terus bertambah karena nilainya tidak musnah di antara dua pemanggilan, berbeda dari variabel lokal yang selalu dimulai dari awal. Kemampuan ini berguna untuk pencacah dan konfigurasi yang dibagi seluruh kelas. Menyimpan data kerja pada field static hanya demi kenyamanan justru membuat aliran data sulit dilacak, sehingga nilai yang dibutuhkan sebuah metode lebih baik dikirim lewat parameter dan dikembalikan lewat return.
Kesalahan umum#
- Memakai variabel perulangan setelah blok
forselesai. Kompilator menjawab cannot find symbol karenaisudah keluar dari lingkupnya. Deklarasikan variabel penampung sebelum perulangan bila hasil akhirnya masih dibutuhkan. - Mengira mengubah parameter yang menutupi field ikut mengubah field. Yang berubah hanya parameternya. Rujuk field lewat nama kelas atau
this, atau ganti nama parameternya. - Mendeklarasikan ulang nama variabel di blok bersarang yang berada di dalam blok dengan nama yang sama. Kompilator menolaknya dengan pesan variable already defined.
- Menyimpan status antarmetode pada field static tanpa kebutuhan. Nilainya terlihat oleh seluruh kelas dan bertahan lebih lama dari yang disangka, sehingga sumber perubahannya sulit ditemukan.
Ringkasan#
- Lingkup menentukan dari mana sebuah nama terbaca, sedangkan masa hidup menentukan lamanya nilai bertahan.
- Parameter hidup sepanjang metode, variabel blok mati bersama bloknya, dan field static bertahan sepanjang program.
- Shadowing membuat nama dalam menutupi nama luar, sedangkan field yang tertutup tetap bisa dirujuk lewat nama kelas atau
this. - Data antarpemanggilan lebih aman dikirim lewat parameter dan nilai kembalian daripada disimpan di field static.
Referensi#
- Scope of a Declaration — JLS, Java SE 21 — definisi lingkup untuk tiap jenis deklarasi, dari lokal sampai field.
- Understanding Class Members — Oracle — perbedaan field kelas dan field objek beserta masa hidup keduanya.