Izinkan saya mengatakan ini dengan sangat jelas, karena orang salah paham sepanjang waktu:
Urutan evaluasi subekspresi tidak bergantung pada asosiativitas dan prioritas . Asosiatif dan prioritas menentukan dalam urutan apa operator dieksekusi tetapi tidak menentukan dalam urutan apa subekspresi dievaluasi. Pertanyaan Anda adalah tentang urutan di mana sub - ekspresi dievaluasi.
Pertimbangkan A() + B() + C() * D()
. Perkalian lebih diutamakan daripada penjumlahan, dan penjumlahan adalah asosiatif kiri, jadi ini setara dengan (A() + B()) + (C() * D())
Tetapi mengetahui bahwa hanya memberi tahu Anda bahwa penjumlahan pertama akan terjadi sebelum penjumlahan kedua, dan perkalian akan terjadi sebelum penjumlahan kedua. Itu tidak memberi tahu Anda dalam urutan apa A (), B (), C () dan D () akan dipanggil! (Ini juga tidak memberi tahu Anda apakah perkalian terjadi sebelum atau setelah penjumlahan pertama.) Sangat mungkin untuk mematuhi aturan presedensi dan asosiativitas dengan menyusun ini sebagai:
d = D()
b = B()
c = C()
a = A()
sum = a + b
product = c * d
result = sum + product
Semua aturan prioritas dan asosiatif diikuti di sana - penjumlahan pertama terjadi sebelum penjumlahan kedua, dan perkalian terjadi sebelum penjumlahan kedua. Jelas kita dapat melakukan panggilan ke A (), B (), C () dan D () dalam urutan apa pun dan tetap mematuhi aturan prioritas dan asosiatif!
Kami memerlukan aturan yang tidak terkait dengan aturan prioritas dan asosiatif untuk menjelaskan urutan subekspresi dievaluasi. Aturan yang relevan di Java (dan C #) adalah "subekspresi dievaluasi dari kiri ke kanan". Karena A () muncul di kiri C (), A () dievaluasi terlebih dahulu, terlepas dari fakta bahwa C () terlibat dalam perkalian dan A () hanya terlibat dalam penjumlahan.
Jadi sekarang Anda memiliki cukup informasi untuk menjawab pertanyaan Anda. Dalam a[b] = b = 0
aturan asosiatif mengatakan bahwa ini a[b] = (b = 0);
tetapi itu tidak berarti bahwa yang b=0
berjalan lebih dulu! Aturan prioritas mengatakan bahwa pengindeksan lebih diutamakan daripada tugas, tetapi itu tidak berarti bahwa pengindeks berjalan sebelum tugas paling kanan .
(PEMBARUAN: Versi sebelumnya dari jawaban ini memiliki beberapa kelalaian kecil dan praktis tidak penting di bagian berikutnya yang telah saya koreksi. Saya juga menulis artikel blog yang menjelaskan mengapa aturan ini masuk akal di Java dan C # di sini: https: // ericlippert.com/2019/01/18/indexer-error-cases/ )
Presedensi dan asosiatif hanya memberi tahu kita bahwa penetapan nol ke b
harus terjadi sebelum penetapan ke a[b]
, karena penetapan nol menghitung nilai yang ditetapkan dalam operasi pengindeksan. Precedence dan associativity saja katakanlah apa-apa tentang apakah a[b]
dievaluasi sebelum atau setelah itu b=0
.
Sekali lagi, ini sama seperti: A()[B()] = C()
- Yang kita tahu adalah bahwa pengindeksan harus dilakukan sebelum penugasan. Kami tidak tahu apakah A (), B (), atau C () berjalan lebih dulu berdasarkan prioritas dan asosiatif . Kami membutuhkan aturan lain untuk memberi tahu kami hal itu.
Aturannya adalah, sekali lagi, "ketika Anda memiliki pilihan tentang apa yang harus dilakukan pertama kali, selalu belok kiri ke kanan". Namun, ada kerutan yang menarik dalam skenario khusus ini. Apakah efek samping dari pengecualian yang dilempar disebabkan oleh koleksi null atau indeks di luar rentang dianggap sebagai bagian dari penghitungan sisi kiri tugas, atau bagian dari penghitungan tugas itu sendiri? Java memilih yang terakhir. (Tentu saja, ini adalah perbedaan yang hanya penting jika kodenya sudah salah , karena kode yang benar tidak mengurangi nol atau meneruskan indeks yang buruk sejak awal.)
Lalu apa yang terjadi?
- Itu
a[b]
adalah di sebelah kiri b=0
, jadi a[b]
run pertama , menghasilkan a[1]
. Namun, pemeriksaan validitas operasi pengindeksan ini tertunda.
- Kemudian
b=0
terjadilah.
- Kemudian verifikasi yang
a
valid dan a[1]
dalam jangkauan terjadi
- Penetapan nilai
a[1]
terjadi terakhir.
Jadi, meskipun dalam kasus khusus ini ada beberapa kehalusan yang perlu dipertimbangkan untuk kasus kesalahan langka yang semestinya tidak terjadi dalam kode yang benar sejak awal, secara umum Anda dapat bernalar: hal-hal di kiri terjadi sebelum hal-hal di kanan . Itulah aturan yang Anda cari. Pembicaraan tentang prioritas dan asosiatif sama-sama membingungkan dan tidak relevan.
Orang-orang melakukan kesalahan ini sepanjang waktu , bahkan orang yang seharusnya lebih tahu. Saya telah menyunting terlalu banyak buku pemrograman yang menyatakan aturannya secara tidak benar, sehingga tidak mengherankan jika banyak orang memiliki keyakinan yang salah sepenuhnya tentang hubungan antara presedensi / asosiativitas, dan urutan evaluasi - yaitu, bahwa pada kenyataannya tidak ada hubungan seperti itu. ; mereka mandiri.
Jika topik ini menarik minat Anda, lihat artikel saya tentang subjek untuk bacaan lebih lanjut:
http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/
Ini tentang C #, tetapi sebagian besar dari hal ini berlaku sama baiknya untuk Java.