a
hanya bisa final di sini. Mengapa? Bagaimana saya dapat menetapkan kembalia
dalamonClick()
metode tanpa menyimpannya sebagai anggota pribadi?private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; } }); }
Bagaimana saya bisa mengembalikan
5 * a
ketika diklik? Maksudku,private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; return b; // but return type is void } }); }
java
event-handling
anonymous-class
Andrii Abramov
sumber
sumber
Jawaban:
Seperti disebutkan dalam komentar, beberapa di antaranya menjadi tidak relevan di Jawa 8, di mana
final
dapat tersirat. Namun, hanya variabel akhir yang efektif yang dapat digunakan dalam kelas batin anonim atau ekspresi lambda.Ini pada dasarnya karena cara Jawa mengelola penutupan .
Saat Anda membuat instance kelas dalam anonim, variabel apa pun yang digunakan dalam kelas itu memiliki nilainya disalin melalui konstruktor yang di-autogenerasi. Ini menghindari kompiler harus autogenerate berbagai tipe tambahan untuk menahan keadaan logis dari "variabel lokal", seperti misalnya kompiler C # tidak ... (Ketika C # menangkap variabel dalam fungsi anonim, itu benar-benar menangkap variabel - yang closure dapat memperbarui variabel dengan cara yang dilihat oleh bagian utama dari metode, dan sebaliknya.)
Karena nilai telah disalin ke instance kelas dalam anonim, akan terlihat aneh jika variabel dapat dimodifikasi oleh sisa metode - Anda bisa memiliki kode yang tampaknya berfungsi dengan variabel out-of-date ( karena itu efektif apa yang akan terjadi ... Anda akan bekerja dengan salinan yang diambil pada waktu yang berbeda). Demikian juga jika Anda bisa membuat perubahan dalam kelas dalam anonim, pengembang mungkin berharap perubahan itu akan terlihat dalam tubuh metode terlampir.
Membuat variabel final menghapus semua kemungkinan ini - karena nilainya tidak dapat diubah sama sekali, Anda tidak perlu khawatir tentang apakah perubahan tersebut akan terlihat. Satu-satunya cara untuk memungkinkan metode dan kelas dalam anonim melihat perubahan masing-masing adalah dengan menggunakan jenis deskripsi yang bisa berubah. Ini bisa berupa kelas penutup itu sendiri, sebuah array, jenis pembungkus yang bisa berubah ... hal seperti itu. Pada dasarnya ini sedikit seperti berkomunikasi antara satu metode dan yang lain: perubahan yang dilakukan pada parameter dari satu metode tidak terlihat oleh pemanggilnya, tetapi perubahan yang dilakukan pada objek yang dirujuk oleh parameter terlihat.
Jika Anda tertarik dengan perbandingan yang lebih terperinci antara penutupan Java dan C #, saya memiliki artikel yang membahasnya lebih lanjut. Saya ingin fokus pada sisi Jawa dalam jawaban ini :)
sumber
final
(tetapi masih perlu efektif akhir).Ada trik yang memungkinkan kelas anonim untuk memperbarui data di lingkup luar.
Namun, trik ini tidak terlalu bagus karena masalah sinkronisasi. Jika handler dipanggil nanti, Anda perlu 1) menyinkronkan akses ke res jika handler dipanggil dari utas yang berbeda 2) perlu memiliki semacam tanda atau indikasi bahwa res telah diperbarui
Trik ini berfungsi baik, jika kelas anonim dipanggil di utas yang sama segera. Suka:
sumber
List.forEach
kode saya aman.Kelas anonim adalah kelas dalam dan aturan ketat berlaku untuk kelas dalam (JLS 8.1.3) :
Saya belum menemukan alasan atau penjelasan tentang jls atau jvms, tetapi kita tahu, bahwa kompiler membuat file kelas terpisah untuk setiap kelas dalam dan itu harus memastikan, bahwa metode dideklarasikan pada file kelas ini ( pada level kode byte) setidaknya memiliki akses ke nilai-nilai variabel lokal.
( Jon memiliki jawaban lengkap - Saya menjaga yang ini tidak terhapus karena orang mungkin tertarik pada aturan JLS)
sumber
Anda dapat membuat variabel level kelas untuk mendapatkan nilai yang dikembalikan. maksudku
sekarang Anda bisa mendapatkan nilai K dan menggunakannya di mana Anda inginkan.
Jawaban mengapa Anda adalah:
Instance kelas dalam lokal terkait dengan kelas Utama dan dapat mengakses variabel lokal akhir dari metode yang berisi. Ketika instance menggunakan lokal final dari metode yang mengandung, variabel mempertahankan nilai yang dipegangnya pada saat pembuatan instance, bahkan jika variabel telah keluar dari ruang lingkup (ini adalah secara efektif Java mentah, versi penutupan terbatas).
Karena kelas dalam lokal bukan anggota kelas atau paket, itu tidak dinyatakan dengan tingkat akses. (Namun, jelaskan bahwa anggotanya sendiri memiliki tingkat akses seperti di kelas normal.)
sumber
Nah, di Jawa, variabel bisa final bukan hanya sebagai parameter, tetapi sebagai bidang tingkat kelas, seperti
atau sebagai variabel lokal, seperti
Jika Anda ingin mengakses dan memodifikasi variabel dari kelas anonim, Anda mungkin ingin menjadikan variabel tersebut sebagai variabel tingkat kelas di kelas yang dilampirkan .
Anda tidak dapat memiliki variabel sebagai final dan memberikannya nilai baru.
final
berarti hanya itu: nilainya tidak dapat diubah dan final.Dan karena sudah final, Java dapat dengan aman menyalinnya ke kelas anonim lokal. Anda tidak mendapatkan referensi ke int (terutama karena Anda tidak dapat memiliki referensi ke primitif seperti int di Jawa, hanya referensi ke Objek ).
Itu hanya menyalin nilai dari ke int implisit yang disebut di kelas anonim Anda.
sumber
static
. Mungkin lebih jelas jika Anda menggunakan "variabel instan" sebagai gantinya.Alasan mengapa akses telah dibatasi hanya untuk variabel final lokal adalah bahwa jika semua variabel lokal dapat diakses maka mereka pertama-tama harus disalin ke bagian terpisah di mana kelas batin dapat memiliki akses ke mereka dan mempertahankan beberapa salinan dari variabel lokal yang bisa berubah dapat menyebabkan data tidak konsisten. Sedangkan variabel akhir tidak berubah dan karenanya sejumlah salinan kepada mereka tidak akan berdampak pada konsistensi data.
sumber
Int
sedang dipindahkan ke dalam penutupan, ubah variabel itu ke instanceIntRef
(pada dasarnyaInteger
pembungkus bisa berubah ). Setiap akses variabel kemudian ditulis ulang sesuai.Untuk memahami alasan pembatasan ini, pertimbangkan program berikut:
The interfaceInstance tetap dalam memori setelah inisialisasi kembali metode, tetapi parameter val tidak. JVM tidak dapat mengakses variabel lokal di luar cakupannya, jadi Java membuat panggilan selanjutnya untuk mencetakInteger berfungsi dengan menyalin nilai val ke bidang implisit dengan nama yang sama dalam interfaceInstance . The interfaceInstance dikatakan telah menangkap nilai dari parameter lokal. Jika parameter tidak final (atau efektif final) nilainya dapat berubah, menjadi tidak sinkron dengan nilai yang ditangkap, berpotensi menyebabkan perilaku tidak intuitif.
sumber
Metode-metode dalam kelas batin anonomyous dapat digunakan baik setelah utas yang menelurkannya telah berakhir. Dalam contoh Anda, kelas dalam akan dipanggil pada utas pengiriman acara dan tidak pada utas yang sama dengan yang membuatnya. Oleh karena itu, ruang lingkup variabel akan berbeda. Jadi untuk melindungi masalah ruang lingkup tugas variabel Anda harus menyatakannya final.
sumber
Ketika kelas dalam anonim didefinisikan dalam tubuh metode, semua variabel yang dinyatakan final dalam lingkup metode tersebut dapat diakses dari dalam kelas batin. Untuk nilai skalar, setelah ditetapkan, nilai variabel akhir tidak dapat berubah. Untuk nilai objek, referensi tidak dapat berubah. Ini memungkinkan kompiler Java untuk "menangkap" nilai variabel pada saat run-time dan menyimpan salinan sebagai bidang di kelas dalam. Setelah metode luar dihentikan dan bingkai tumpukannya telah dihapus, variabel asli hilang tetapi salinan pribadi kelas dalam tetap ada di memori kelas itu sendiri.
( http://en.wikipedia.org/wiki/Final_%28Java%29 )
sumber
sumber
Karena Jon memiliki rincian implementasi, jawaban lain yang mungkin adalah bahwa JVM tidak ingin menangani catatan tertulis yang telah mengakhiri aktivasinya.
Pertimbangkan kasus penggunaan di mana lambda Anda alih-alih diterapkan, disimpan di beberapa tempat dan dijalankan kemudian.
Saya ingat bahwa di Smalltalk Anda akan mendapatkan toko ilegal ketika Anda melakukan modifikasi tersebut.
sumber
Coba kode ini,
Buat Daftar Array dan berikan nilai di dalamnya dan kembalikan:
sumber
Kelas anonim Java sangat mirip dengan penutupan Javascript, tetapi Java mengimplementasikannya dengan cara yang berbeda. (periksa jawaban Andersen)
Jadi, agar tidak membingungkan Pengembang Java dengan perilaku aneh yang mungkin terjadi bagi mereka yang berasal dari latar belakang Javascript. Saya kira itu sebabnya mereka memaksa kami untuk menggunakan
final
, ini bukan batasan JVM.Mari kita lihat contoh Javascript di bawah ini:
Dalam Javascript,
counter
nilainya akan menjadi 100, karena hanya ada satucounter
variabel dari awal hingga akhir.Tetapi di Jawa, jika tidak ada
final
, itu akan dicetak0
, karena ketika objek batin sedang dibuat,0
nilainya disalin ke properti tersembunyi objek kelas dalam itu. (ada dua variabel integer di sini, satu di metode lokal, satu lagi di properti tersembunyi kelas dalam)Jadi setiap perubahan setelah pembuatan objek dalam (seperti baris 1), itu tidak akan mempengaruhi objek dalam. Jadi itu akan membuat kebingungan antara dua hasil dan perilaku yang berbeda (antara Java dan Javascript).
Saya percaya itu sebabnya, Jawa memutuskan untuk memaksanya menjadi final, sehingga datanya 'konsisten' dari awal hingga akhir.
sumber
Variabel akhir Java di dalam kelas batin
kelas batin hanya bisa digunakan
Object
...)int
...) dapat dibungkus dengan tipe referensi final.IntelliJ IDEA
dapat membantu Anda menyamarkannya ke satu elemen arrayKetika a
non static nested
(inner class
) [Tentang] dihasilkan oleh kompiler - kelas baru -<OuterClass>$<InnerClass>.class
dibuat dan parameter terikat dilewatkan ke konstruktor [Variabel lokal pada tumpukan] . Ini mirip dengan penutupanvariabel akhir adalah variabel yang tidak dapat ditetapkan kembali. variabel referensi akhir masih dapat diubah dengan memodifikasi keadaan
Mungkin saja akan aneh karena sebagai programmer Anda bisa membuatnya seperti ini
sumber
Mungkin trik ini memberi Anda ide
sumber