tl; dr
Untuk bidang , int b = b + 1ilegal karena bmerupakan 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 + 1ilegal karena dtidak 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 btidak valid dan gagal dengan illegal forward referencekesalahan.
Deklarasi dtidak valid dan gagal dengan variable d might not have been initializedkesalahan.
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
mdideklarasikan 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 asebelum a's deklarasi lengkap.
int b = this.b + 1juga mengkompilasi karena melanggar (c): referensi this.bbukanlah nama yang sederhana (itu memenuhi syarat dengan this.). Konstruksi ganjil ini masih terdefinisi dengan baik, karena this.bmemiliki nilai nol.
Jadi, pada dasarnya, pembatasan referensi bidang dalam penginisialisasi mencegah int a = a + 1agar tidak berhasil dikompilasi.
Perhatikan bahwa deklarasi lapangan int b = (b = 1) + bakan gagal untuk dikompilasi, karena final bmasih 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, xharus ditetapkan dengan pasti sebelum akses, atau kesalahan waktu kompilasi terjadi.
Dalam int d = d + 1;, akses ke ddiselesaikan ke denda variabel lokal, tetapi karena dbelum ditetapkan sebelum ddiakses, kompilator mengeluarkan kesalahan. Dalam int c = c = 1, c = 1terjadi pertama, yang menetapkan c, dan kemudian cdiinisialisasi 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 dpasti ditetapkan pada saat final dtercapai.
staticdalam 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.