tl; dr
Untuk bidang , int b = b + 1
ilegal karena b
merupakan rujukan penerusan ilegal b
. Anda sebenarnya dapat memperbaikinya dengan menulis int b = this.b + 1
, yang mengkompilasi tanpa keluhan.
Untuk variabel lokal , int d = d + 1
ilegal karena d
tidak diinisialisasi sebelum digunakan. Ini tidak terjadi untuk bidang, yang selalu diinisialisasi default.
Anda dapat melihat perbedaannya dengan mencoba mengompilasi
int x = (x = 1) + x;
sebagai deklarasi lapangan dan sebagai deklarasi variabel lokal. Yang pertama akan gagal, tetapi yang terakhir akan berhasil, karena perbedaan semantik.
pengantar
Pertama, aturan untuk bidang dan penginisialisasi variabel lokal sangat berbeda. Jadi jawaban ini akan membahas aturan dalam dua bagian.
Kami akan menggunakan program tes ini selama:
public class test {
int a = a = 1;
int b = b + 1;
public static void Main(String[] args) {
int c = c = 1;
int d = d + 1;
}
}
Deklarasi b
tidak valid dan gagal dengan illegal forward reference
kesalahan.
Deklarasi d
tidak valid dan gagal dengan variable d might not have been initialized
kesalahan.
Fakta bahwa kesalahan ini berbeda seharusnya mengisyaratkan bahwa alasan kesalahan juga berbeda.
Fields
Penginisialisasi bidang di Jawa diatur oleh JLS §8.3.2 , Inisialisasi Bidang.
The lingkup bidang didefinisikan di JLS §6.3 , Lingkup Deklarasi.
Aturan yang relevan adalah:
- Cakupan deklarasi anggota yang
m
dideklarasikan atau diwarisi oleh kelas tipe C (§8.1.6) adalah seluruh isi C, termasuk deklarasi tipe bertingkat.
- Ekspresi inisialisasi untuk variabel instan dapat menggunakan nama sederhana dari variabel statis yang dideklarasikan atau diwarisi oleh kelas, bahkan yang deklarasinya muncul secara tekstual kemudian.
- Penggunaan variabel instan yang deklarasinya muncul secara tekstual setelah penggunaan terkadang dibatasi, meskipun variabel instan ini masih dalam cakupan. Lihat §8.3.2.3 untuk aturan yang tepat yang mengatur referensi maju ke variabel instan.
§8.3.2.3 mengatakan:
Deklarasi anggota harus muncul secara tekstual sebelum digunakan hanya jika anggota tersebut adalah bidang instance (masing-masing statis) dari kelas atau antarmuka C dan semua kondisi berikut berlaku:
- Penggunaan terjadi dalam inisialisasi variabel instance (masing-masing statis) dari C atau dalam inisialisasi instance (masing-masing statis) dari C.
- Penggunaan tidak di sisi kiri tugas.
- Penggunaannya melalui nama yang sederhana.
- C adalah kelas atau antarmuka terdalam yang melingkupi penggunaan.
Anda sebenarnya dapat merujuk ke bidang sebelum dideklarasikan, kecuali dalam kasus tertentu. Pembatasan ini dimaksudkan untuk mencegah kode like
int j = i;
int i = j;
dari kompilasi. Spesifikasi Java mengatakan "pembatasan di atas dirancang untuk menangkap, pada waktu kompilasi, inisialisasi melingkar atau dalam format yang salah".
Apa tujuan sebenarnya dari aturan-aturan ini?
Singkatnya, aturan pada dasarnya mengatakan bahwa Anda harus mendeklarasikan bidang sebelum referensi ke bidang itu jika (a) referensi ada dalam penginisialisasi, (b) referensi tidak ditugaskan, (c) referensi adalah a nama sederhana (tidak ada kualifikasi seperti this.
) dan (d) itu tidak diakses dari dalam kelas dalam. Jadi, referensi maju yang memenuhi keempat kondisi adalah ilegal, tetapi referensi ke depan yang gagal pada setidaknya satu kondisi tidak masalah.
int a = a = 1;
mengkompilasi karena melanggar (b): referensi a
yang sedang ditugaskan untuk, sehingga hukum untuk merujuk a
sebelum a
's deklarasi lengkap.
int b = this.b + 1
juga mengkompilasi karena melanggar (c): referensi this.b
bukanlah nama yang sederhana (itu memenuhi syarat dengan this.
). Konstruksi ganjil ini masih terdefinisi dengan baik, karena this.b
memiliki nilai nol.
Jadi, pada dasarnya, pembatasan referensi bidang dalam penginisialisasi mencegah int a = a + 1
agar tidak berhasil dikompilasi.
Perhatikan bahwa deklarasi lapangan int b = (b = 1) + b
akan gagal untuk dikompilasi, karena final b
masih merupakan referensi penerusan yang ilegal.
Variabel lokal
Deklarasi variabel lokal diatur oleh JLS §14.4 , Pernyataan Deklarasi Variabel Lokal.
The lingkup dari variabel lokal didefinisikan dalam JLS §6.3 , Lingkup Deklarasi:
- Cakupan deklarasi variabel lokal dalam sebuah blok (§14.4) adalah blok lainnya di mana deklarasi tersebut muncul, dimulai dengan penginisialisasinya sendiri dan termasuk setiap deklarator di sebelah kanan dalam pernyataan deklarasi variabel lokal.
Perhatikan bahwa penginisialisasi berada dalam cakupan variabel yang dideklarasikan. Jadi mengapa tidak int d = d + 1;
dikompilasi?
Alasannya karena aturan Jawa tentang penetapan pasti ( JLS §16 ). Penugasan pasti pada dasarnya mengatakan bahwa setiap akses ke variabel lokal harus memiliki penugasan sebelumnya ke variabel itu, dan kompiler Java memeriksa loop dan cabang untuk memastikan bahwa penugasan selalu terjadi sebelum penggunaan apa pun (inilah mengapa penugasan tertentu memiliki seluruh bagian spesifikasi yang didedikasikan untuk itu). Aturan dasarnya adalah:
- Untuk setiap akses variabel lokal atau bidang akhir kosong
x
, x
harus ditetapkan dengan pasti sebelum akses, atau kesalahan waktu kompilasi terjadi.
Dalam int d = d + 1;
, akses ke d
diselesaikan ke denda variabel lokal, tetapi karena d
belum ditetapkan sebelum d
diakses, kompilator mengeluarkan kesalahan. Dalam int c = c = 1
, c = 1
terjadi pertama, yang menetapkan c
, dan kemudian c
diinisialisasi ke hasil tugas itu (yaitu 1).
Perhatikan bahwa karena aturan penugasan yang pasti, deklarasi variabel lokal int d = (d = 1) + d;
akan berhasil dikompilasi ( tidak seperti deklarasi lapangan int b = (b = 1) + b
), karena d
pasti ditetapkan pada saat final d
tercapai.
static
dalam variabel cakupan kelas, seperti dalamstatic int x = x + 1;
, apakah Anda akan mendapatkan kesalahan yang sama? Karena di C # itu membuat perbedaan apakah itu statis atau non-statis.