Pertanyaan Mengapa variabel lokal untuk loop yang ditingkatkan harus lokal? [duplikat]


Pertanyaan ini sudah memiliki jawaban di sini:

Menurut Spesifikasi Bahasa Java, § 14.14.2, variabel yang ditingkatkan for loop harus lokal ke loop. Dengan kata lain, ini mengkompilasi:

for (State state : State.values()) {
    // do something for each state
}

tetapi ini tidak:

State state;
for (state: State.values()) {
    // do something for each state
}

The JLS tidak memberikan alasan untuk pilihan desain bahasa ini. Saya dapat melihat mengapa nama jenis harus ada jika variabel lokal dimodifikasi oleh final atau dengan anotasi, tetapi saya tidak mengerti mengapa nama variabel yang disebutkan di tempat lain tidak diizinkan. Apakah ada yang tahu mengapa pembatasan ini diberlakukan?

EDIT

Beberapa jawaban sejauh ini tampaknya menunjukkan bahwa apa yang terjadi di luar lingkaran adalah alasan untuk merancang bahasa dengan cara ini. Mungkin pemeriksaan lebih dekat dari apa yang dikatakan JLS akan menjelaskan mengapa saya tidak menemukan ini meyakinkan. Pertimbangkan lingkaran ini, di mana State adalah enum:

for (State state : State.values()) {
    // ...
}

State.values() adalah array, jadi menurut JLS, loop secara fungsional identik dengan:

State[] a = State.values();
for (int i = 0; i < a.length; i++) {
    State state = a[i];
    // ...
}

Sekarang jelas lingkaran terakhir ini dapat ditulis:

State state;
State[] a = State.values();
for (int i = 0; i < a.length; i++) {
    state = a[i];
    // ...
}

Secara konseptual, lingkaran terakhir (yang sah) ini dapat digunakan sebagai ekuivalen fungsional dari peningkatan kedua for loop di atas (yang tidak dikompilasi).

Demikian pula, jika stateList adalah Iterable<State> (bukan array), loop ini:

for (State state : stateList) {
    // ...
}

secara fungsional identik dengan:

for (Iterator<State> iterator = stateList.iterator(); iterator.hasNext(); ) {
    State state = iterator.next();
    // ...
}

Seperti sebelumnya, loop terakhir ini bisa ditulis:

State state;
for (Iterator<State> iterator = stateList.iterator(); iterator.hasNext(); ) {
    state = iterator.next();
    // ...
}

Sekali lagi, ini bisa telah digunakan sebagai padanan fungsional dari (ilegal):

State state;
for (state : stateList) {
    // ...
}

Dalam setiap kasus, ketika loop keluar, nilai dari state didefinisikan dengan sangat baik (jika, mungkin, tidak berguna). Juga, sama seperti loop biasa, yang ditingkatkan for loop menggunakan nama variabel telanjang yang tidak didefinisikan (misalnya, garis State state; hilang atau keluar dari ruang lingkup) dapat ditangkap pada waktu kompilasi. Jadi dari perspektif desain bahasa, apa masalahnya? Mengapa desainer bahasa melarang konstruksi ini?


32
2017-11-20 20:09


asal


Jawaban:


Satu manfaat / alasan adalah variabel lokal tidak mencemari kode Anda. Biarkan saya memberikan contoh loop normal (ini hanya untuk analogi bukan yang tepat, jadi tidak menggunakan iterator):

int i;
for(i=0;i<10;i++)
  do...something

int j;
for(j=0; i<10; j++)
  do...something

Sekarang dalam kode di atas jika melihat lebih dekat Anda akan menemukan bug potensial. i telah salah digunakan dalam lingkaran yang berulang j.

Jadi loop yang disempurnakan mencoba untuk bermain aman dengan membuat variabel secara lokal, dengan mana Anda dapat menghindari masalah di atas.


9
2017-11-20 23:57



Lihatlah bagaimana setiap loop bekerja secara internal, lihat Bagaimana cara kerja Java 'for each' loop?

for(Iterator<String> i = someList.iterator(); i.hasNext(); ) {
String item = i.next();
System.out.println(item);
}

Setiap kali menyatakan item variabel String. Oleh karena itu dalam kasus Anda yang melakukan dasarnya

State state;
\\and inside
State state = i.next();

yang jelas tidak akan berhasil. Sekarang di dalam implementasi, mereka hanya bisa melakukannya

item = i.next();

tetapi kemudian Anda harus selalu mendefinisikan item itu di luar untuk masing-masing, yang akan menjadi mayoritas rasa sakit waktu.


11
2017-11-20 20:20



Banyak keputusan di Jawa lebih didasarkan pada konsep mengapa "Tidak" Anda menghapus x. Mengapa memungkinkan kode Anda menjadi bingung dengan memindahkan ruang lingkup di luar loop? Jika Anda benar-benar membutuhkan akses ke elemen terakhir setelah itu ada cara yang lebih bersih.

Saya kira beberapa orang mungkin memperdebatkan satu atau lain cara untuk melindungi programmer dari diri mereka sendiri, saya melihatnya lebih sebagai Java melindungi saya dari orang-orang yang suka mengambil keuntungan dari hal-hal seperti ini.

Ini bukan benar-benar bahasa hobi yang hebat, itu adalah bahasa pengembangan tim yang hebat di mana intinya adalah untuk membuatnya semudah mungkin bagi orang berikutnya untuk memahami kode Anda.

Sebagai contoh, saya melihat dalam komentar di atas bahwa banyak orang keluar dari lingkaran pada titik tertentu dengan nilai. Seberapa jelaskah itu ketika Anda memindai kode mencoba menemukan bug? Orang-orang menggunakan trik rapi seperti ini yang sangat ingin saya lindungi, bukan diri saya sendiri.

Jika Anda ingin menjalankan beberapa kode mid-loop, letakkan dalam metode dan memanggilnya dari dalam loop.

Kurangnya trik lucu sering membuat saya berpikir ulang kode saya dan bekerja sedikit lebih keras - tetapi hasilnya selalu lebih mudah dibaca / dipelihara.

Jika Anda hanya seorang programmer tunggal (Bukan dalam tim) saya akan menyarankan melawan Java, itu tidak melakukan trik rapi dan Anda tidak akan melihat pembaruan fitur tahunan keren - dan itu cukup lambat untuk memprogram dalam (Ini bergerak banyak waktu menangkap dan memperbaiki bug ke dalam waktu yang dihabiskan coding, membuat frustrasi orang dengan aturan yang ketat (Salah satu keuntungan terbesar dari bahasa IMO)

Jika Anda menginginkan sesuatu, cobalah Javaish Scala sebagai gantinya. Jika Anda "Terpaksa" untuk mempelajarinya karena keputusan kelas, Anda mungkin ingin mencoba menghargainya dari sudut pandang "tim" dan dengan pola pikir bahwa Anda tidak akan dipaksa untuk menggunakannya di masa depan.


7
2017-11-21 05:39



Mereka mungkin ingin menghapus perbedaan yang mungkin dalam semantik antara yang sederhana untuk dan yang ditingkatkan.

Jika Anda memiliki, katakanlah, lingkaran biasa:

int i;
for (i=0; i<4; i++)
    ;

maka jika Anda membiarkan loop berjalan dengan normal, Anda mendapatkan itu i==4 setelah loop, yang tidak valid untuk variabel iterasi.

Lalu, jika Anda bisa:

int i;
for (i : myArray)
    ;

Saya kira dari sudut pandang implementasi, akan lebih mudah jika pada akhirnya i sama dengan elemen terakhir dalam larik. Tetapi haruskah itu berperilaku seperti itu atau seharusnya ada beberapa nilai yang tidak valid, dan jika ya, apa yang bisa terjadi? Bagaimana jika Anda keluar dari iterasi terakhir?

Kemungkinan lain adalah itu membuatnya lebih jelas tentang apa lingkaran itu dan apa jenis elemen dari koleksi itu. Sebaliknya, sederhana untuk adalah "warisan" membangun dalam arti, sehingga mereka tidak bisa menerapkan logika yang sama tanpa "melanggar" atau membatasi fleksibilitasnya.

Saya jelas berspekulasi, dan itu bisa berubah menjadi pilihan desain acak.


5
2017-11-20 21:08



Mungkin karena dengan cara ini, itu menjamin itu state akan kosong dan tidak memiliki nilai awal yang ditetapkan untuk itu.


3
2017-11-20 20:17