Dalam metode atau ruang lingkup kelas, baris di bawah ini dikompilasi (dengan peringatan):
int x = x = 1;
Dalam ruang lingkup kelas, di mana variabel mendapatkan nilai defaultnya , berikut ini memberikan kesalahan 'referensi tidak terdefinisi':
int x = x + 1;
Bukankah yang pertama x = x = 1
harus berakhir dengan kesalahan 'referensi tidak terdefinisi' yang sama? Atau mungkin baris kedua int x = x + 1
harus dikompilasi? Atau ada sesuatu yang saya lewatkan?
java
compiler-construction
Marcin
sumber
sumber
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.static int x = x + 1
gagal di Java.int a = this.a + 1;
danint b = 1; int a = b + 1;
dalam cakupan kelas (keduanya baik-baik saja di Java) gagal, mungkin karena §17.4.5.2 - "Penginisialisasi variabel untuk bidang instance tidak dapat mereferensikan instance yang sedang dibuat." Saya tidak tahu apakah itu secara eksplisit diizinkan di suatu tempat tetapi statis tidak memiliki batasan seperti itu. Di Java aturannya berbeda danstatic int x = x + 1
gagal karena alasan yangint x = x + 1
sama sepertiJawaban:
tl; dr
Untuk bidang ,
int b = b + 1
ilegal karenab
merupakan rujukan penerusan ilegalb
. Anda sebenarnya dapat memperbaikinya dengan menulisint b = this.b + 1
, yang mengkompilasi tanpa keluhan.Untuk variabel lokal ,
int d = d + 1
ilegal karenad
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:
Deklarasi
b
tidak valid dan gagal denganillegal forward reference
kesalahan.Deklarasi
d
tidak valid dan gagal denganvariable 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:
m
dideklarasikan atau diwarisi oleh kelas tipe C (§8.1.6) adalah seluruh isi C, termasuk deklarasi tipe bertingkat.§8.3.2.3 mengatakan:
Anda sebenarnya dapat merujuk ke bidang sebelum dideklarasikan, kecuali dalam kasus tertentu. Pembatasan ini dimaksudkan untuk mencegah kode like
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): referensia
yang sedang ditugaskan untuk, sehingga hukum untuk merujuka
sebeluma
's deklarasi lengkap.int b = this.b + 1
juga mengkompilasi karena melanggar (c): referensithis.b
bukanlah nama yang sederhana (itu memenuhi syarat denganthis.
). Konstruksi ganjil ini masih terdefinisi dengan baik, karenathis.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 finalb
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:
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:
x
,x
harus ditetapkan dengan pasti sebelum akses, atau kesalahan waktu kompilasi terjadi.Dalam
int d = d + 1;
, akses ked
diselesaikan ke denda variabel lokal, tetapi karenad
belum ditetapkan sebelumd
diakses, kompilator mengeluarkan kesalahan. Dalamint c = c = 1
,c = 1
terjadi pertama, yang menetapkanc
, dan kemudianc
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 lapanganint b = (b = 1) + b
), karenad
pasti ditetapkan pada saat finald
tercapai.sumber
int b = b + 1
b ada di sebelah kanan (bukan di kiri) tugas sehingga akan melanggar ini ...int x = x = 1
, di mana jika semua ini tidak akan berlaku.setara dengan
saat di
pertama kita perlu menghitung
x+1
tetapi nilai x tidak diketahui sehingga Anda mendapatkan kesalahan (penyusun tahu bahwa nilai x tidak diketahui)sumber
int x = x = 1;
setara denganint x = (x = 1)
, tidakx = 1; x = x;
. Anda seharusnya tidak mendapatkan peringatan kompiler untuk melakukan ini.int x = x = 1;
s setara dengan intx = (x = 1)
karena asosiasi kanan=
operatorint x = (x = 1)
setara denganint x; x = 1; x = x;
(deklarasi variabel, evaluasi penginisialisasi lapangan, penugasan variabel ke hasil evaluasi tersebut), maka peringatanIni kira-kira setara dengan:
Pertama,
int <var> = <expression>;
selalu setara denganDalam hal ini, ekspresi Anda adalah
x = 1
, yang juga merupakan pernyataan.x = 1
adalah pernyataan yang valid, karena varx
telah dideklarasikan. Ini juga merupakan ekspresi dengan nilai 1, yang kemudian ditetapkanx
lagi.sumber
0
nilai default untuk int, jadi saya mengharapkan hasilnya menjadi 1, bukanundefined reference
.x + 1
tidak memiliki nilai yang ditentukan, karenax
tidak diinisialisasi.x
ini didefinisikan sebagai variabel anggota ( "dalam lingkup kelas").Di java atau dalam bahasa modern apa pun, penugasan berasal dari kanan.
Misalkan jika Anda memiliki dua variabel x dan y,
Pernyataan ini valid dan begitulah cara compiler membaginya.
Tapi dalam kasusmu
Kompilator memberikan pengecualian karena terbagi seperti ini.
sumber
int x = x = 1;
tidak sama dengan:javap membantu kami lagi, ini adalah instruksi JVM yang dihasilkan untuk kode ini:
lebih seperti:
Tidak ada alasan untuk menampilkan kesalahan referensi yang tidak ditentukan. Sekarang ada penggunaan variabel sebelum inisialisasi, jadi kode ini sepenuhnya sesuai dengan spesifikasi. Faktanya tidak ada penggunaan variabel sama sekali , hanya tugas. Dan compiler JIT akan melangkah lebih jauh, ini akan menghilangkan konstruksi seperti itu. Sejujurnya, saya tidak mengerti bagaimana kode ini terhubung ke spesifikasi inisialisasi dan penggunaan variabel JLS. Tidak ada penggunaan, tidak ada masalah. ;)
Harap perbaiki jika saya salah. Saya tidak tahu mengapa jawaban lain, yang mengacu pada banyak paragraf JLS mengumpulkan begitu banyak nilai tambah. Paragraf-paragraf ini tidak memiliki kesamaan dengan kasus ini. Hanya dua tugas serial dan tidak lebih.
Jika kita menulis:
adalah sama dengan:
Ekspresi paling kanan hanya diberikan ke variabel satu per satu, tanpa rekursi apa pun. Kita bisa mengacaukan variabel sesuka kita:
sumber
Jika
int x = x + 1;
Anda menambahkan 1 ke x, jadi berapa nilainyax
, itu belum dibuat.Tetapi di
int x=x=1;
akan mengkompilasi tanpa kesalahan karena Anda menetapkan 1 untukx
.sumber
Potongan kode pertama Anda berisi yang kedua,
=
bukan plus. Ini akan dikompilasi di mana saja sementara potongan kode kedua tidak akan dikompilasi di tempat mana pun.sumber
Di bagian kode kedua, x digunakan sebelum deklarasinya, sedangkan di bagian pertama kode itu hanya ditetapkan dua kali yang tidak masuk akal tetapi valid.
sumber
Mari kita uraikan langkah demi langkah, asosiatif yang benar
x = 1
, tetapkan 1 ke variabel xint x = x
, tetapkan apa x untuk dirinya sendiri, sebagai int. Karena x sebelumnya ditetapkan sebagai 1, ia mempertahankan 1, meskipun dengan cara yang berlebihan.Itu mengkompilasi dengan baik.
x + 1
, tambahkan satu ke variabel x. Namun, x yang tidak ditentukan ini akan menyebabkan kesalahan kompilasi.int x = x + 1
, sehingga kesalahan kompilasi baris ini karena bagian kanan dari sama tidak akan dikompilasi menambahkan satu ke variabel yang tidak ditetapkansumber
=
operator, jadi sama denganint x = (x = 1);
.Yang kedua
int x=x=1
adalah kompilasi karena Anda menetapkan nilai ke x tetapi dalam kasus lain diint x=x+1
sini variabel x tidak diinisialisasi, Ingat dalam java variabel lokal tidak diinisialisasi ke nilai default. Catatan Jika it's (int x=x+1
) dalam ruang lingkup kelas juga maka itu juga akan memberikan kesalahan kompilasi karena variabel tidak dibuat.sumber
berhasil dikompilasi di Visual Studio 2008 dengan peringatan
sumber
c
bukannyajava
tapi rupanya itu adalah pertanyaan lain.bool y;
dany==true
akan mengembalikan false.void main() { int x = x + 1; printf("%d ", x); }
di Visual Studio 2008, di Debug saya mendapatkan pengecualianRun-Time Check Failure #3 - The variable 'x' is being used without being initialized.
dan di Rilis saya mendapatkan nomor yang1896199921
dicetak di konsol.static
bidang (variabel statis tingkat kelas), aturan yang sama berlaku. Misalnya bidang dideklarasikan sebagaipublic static int x = x + 1;
kompilasi tanpa peringatan dalam Visual C #. Mungkin sama di Jawa?x tidak diinisialisasi dalam
x = x + 1
;.Bahasa pemrograman Java diketik secara statis, yang artinya semua variabel harus dideklarasikan terlebih dahulu sebelum dapat digunakan.
Lihat tipe data primitif
sumber
Baris kode tidak dapat dikompilasi dengan peringatan karena cara kerja kode sebenarnya. Saat Anda menjalankan kode
int x = x = 1
, Java akan membuat variabel terlebih dahulux
, seperti yang ditentukan. Kemudian menjalankan kode tugas (x = 1
). Karenax
sudah ditentukan, sistem tidak memiliki kesalahan pengaturanx
ke 1. Ini mengembalikan nilai 1, karena itu sekarang adalah nilaix
. Oleh karena itu,x
sekarang akhirnya ditetapkan sebagai 1.Java pada dasarnya mengeksekusi kode seolah-olah seperti ini:
Namun, dalam potongan kode kedua Anda
int x = x + 1
,,+ 1
pernyataan itu perlux
didefinisikan, yang pada saat itu belum. Karena pernyataan penugasan selalu berarti kode di sebelah kanan=
dijalankan pertama kali, kode akan gagal karenax
tidak ditentukan. Java akan menjalankan kode seperti ini:sumber
Pernyataan membaca yang lebih lengkap dari kanan ke kiri dan kami merancang untuk melakukan yang sebaliknya. Karena itulah awalnya dia kesal. Jadikan ini kebiasaan membaca pernyataan (kode) dari kanan ke kiri Anda tidak akan mengalami masalah seperti itu.
sumber