Diedit: Saya perlu mengubah nilai beberapa variabel karena mereka menjalankan beberapa kali thorugh timer. Saya harus terus memperbarui nilai dengan setiap iterasi melalui penghitung waktu. Saya tidak dapat menetapkan nilai ke final karena itu akan mencegah saya memperbarui nilai namun saya mendapatkan kesalahan yang saya jelaskan dalam pertanyaan awal di bawah ini:
Saya sebelumnya telah menulis apa yang ada di bawah ini:
Saya mendapatkan kesalahan "tidak dapat merujuk ke variabel non-final di dalam kelas batin yang didefinisikan dalam metode yang berbeda".
Ini terjadi untuk harga yang disebut dobel dan Harga yang disebut priceObject. Apakah Anda tahu mengapa saya mendapatkan masalah ini? Saya tidak mengerti mengapa saya harus memiliki deklarasi akhir. Juga jika Anda dapat melihat apa yang saya coba lakukan, apa yang harus saya lakukan untuk mengatasi masalah ini.
public static void main(String args[]) {
int period = 2000;
int delay = 2000;
double lastPrice = 0;
Price priceObject = new Price();
double price = 0;
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
price = priceObject.getNextPrice(lastPrice);
System.out.println();
lastPrice = price;
}
}, delay, period);
}
sumber
Jawaban:
Java tidak mendukung penutupan yang sebenarnya , meskipun menggunakan kelas anonim seperti yang Anda gunakan di sini (
new TimerTask() { ... }
) terlihat seperti semacam penutupan.sunting - Lihat komentar di bawah ini - berikut ini bukan penjelasan yang benar, seperti yang ditunjukkan KeeperOfTheSoul.
Inilah mengapa itu tidak berhasil:
Variabel
lastPrice
dan harga adalah variabel lokal dalam metode main (). Objek yang Anda buat dengan kelas anonim mungkin bertahan hingga setelahmain()
metode kembali.Ketika
main()
metode kembali, variabel lokal (sepertilastPrice
danprice
) akan dibersihkan dari tumpukan, sehingga mereka tidak akan ada lagi setelahmain()
kembali.Tetapi objek kelas anonim referensi variabel-variabel ini. Hal-hal buruk akan menjadi salah jika objek kelas anonim mencoba mengakses variabel setelah mereka dibersihkan.
Dengan membuat
lastPrice
danprice
final
, mereka bukan benar-benar variabel lagi, tetapi konstanta. Compiler kemudian dapat mengganti penggunaanlastPrice
danprice
di kelas anonim dengan nilai-nilai konstanta (pada waktu kompilasi, tentu saja), dan Anda tidak akan memiliki masalah dengan mengakses variabel yang tidak ada lagi.Bahasa pemrograman lain yang mendukung penutupan melakukannya dengan memperlakukan variabel-variabel tersebut secara khusus - dengan memastikan mereka tidak hancur ketika metode berakhir, sehingga penutupan masih dapat mengakses variabel.
@Ankur: Anda bisa melakukan ini:
sumber
Untuk menghindari efek samping yang aneh dengan penutupan dalam variabel java yang dirujuk oleh delegasi anonim harus ditandai sebagai final, jadi untuk merujuk
lastPrice
dan harga dalam tugas timer mereka harus ditandai sebagai final.Ini jelas tidak akan berfungsi untuk Anda karena Anda ingin mengubahnya, dalam hal ini Anda harus melihat merangkum mereka dalam kelas.
sekarang cukup buat Foo baru sebagai final dan panggil .tick dari timer.
sumber
Anda hanya dapat mengakses variabel akhir dari kelas yang berisi ketika menggunakan kelas anonim. Oleh karena itu Anda perlu untuk mendeklarasikan variabel yang digunakan akhir (yang bukan merupakan pilihan bagi Anda karena Anda mengubah lastPrice dan harga ), atau tidak menggunakan kelas anonim.
Jadi, pilihan Anda adalah membuat kelas dalam yang sebenarnya, di mana Anda bisa meneruskan variabel dan menggunakannya secara normal
atau:
Ada hack cepat (dan menurut saya jelek) untuk variabel LastPrice dan harga Anda yang menyatakan seperti itu
dan di kelas anonim Anda, Anda dapat mengatur nilai seperti ini
sumber
Penjelasan yang baik untuk mengapa Anda tidak dapat melakukan apa yang Anda coba lakukan sudah disediakan. Sebagai solusi, mungkin pertimbangkan:
Sepertinya Anda bisa melakukan desain yang lebih baik dari itu, tetapi idenya adalah Anda bisa mengelompokkan variabel yang diperbarui di dalam referensi kelas yang tidak berubah.
sumber
Dengan kelas anonim, Anda sebenarnya mendeklarasikan kelas bersarang "tanpa nama". Untuk kelas bertingkat, kompiler menghasilkan kelas publik mandiri baru dengan konstruktor yang akan mengambil semua variabel yang digunakan sebagai argumen (untuk kelas bertingkat "bernama", ini selalu merupakan turunan dari kelas asli / tertutup). Ini dilakukan karena lingkungan runtime tidak memiliki gagasan tentang kelas bersarang, sehingga perlu ada konversi (otomatis) dari kelas bersarang ke kelas mandiri.
Ambil kode ini misalnya:
Itu tidak akan berhasil, karena inilah yang dilakukan oleh kompiler di bawah tenda:
Kelas anonim asli digantikan oleh beberapa kelas mandiri yang dihasilkan oleh kompiler (kode tidak tepat, tetapi harus memberi Anda ide yang baik):
Seperti yang Anda lihat, kelas mandiri memegang referensi ke objek bersama, ingat bahwa semua yang ada di java adalah nilai per nilai, jadi meskipun variabel referensi 'dibagi' di EnclosingClass akan diubah, contoh yang ditunjukkannya tidak dimodifikasi. , dan semua variabel referensi lainnya yang menunjuk ke sana (seperti yang ada di kelas anonim: Melampirkan $ 1), tidak akan mengetahui hal ini. Ini adalah alasan utama kompilator memaksa Anda untuk mendeklarasikan variabel 'dibagikan' ini sebagai final, sehingga jenis perilaku ini tidak akan membuatnya menjadi kode yang sudah berjalan.
Sekarang, inilah yang terjadi ketika Anda menggunakan variabel instan di dalam kelas anonim (inilah yang harus Anda lakukan untuk menyelesaikan masalah Anda, pindahkan logika Anda ke metode "instance" atau konstruktor suatu kelas):
Ini mengkompilasi dengan baik, karena kompiler akan mengubah kode, sehingga kelas baru yang dihasilkan Menutupi $ 1 akan menyimpan referensi ke instance EnclosingClass di mana ia dipakai (ini hanya representasi, tetapi harus membuat Anda pergi):
Seperti ini, ketika variabel referensi 'dibagikan' di EnclosingClass akan dipindahkan, dan ini terjadi sebelum panggilan ke Thread # run (), Anda akan melihat "halo lainnya" dicetak dua kali, karena sekarang variabel penutup EnclosingClass $ 1 # akan menyimpan referensi ke objek kelas yang dideklarasikan, jadi perubahan pada atribut apa pun pada objek itu akan terlihat oleh instance dari EnclosingClass $ 1.
Untuk informasi lebih lanjut tentang hal ini, Anda dapat melihat posting blog yang sangat baik ini (tidak ditulis oleh saya): http://kevinboone.net/java_inner.html
sumber
Ketika saya menemukan masalah ini, saya hanya meneruskan objek ke kelas batin melalui konstruktor. Jika saya harus melewati primitif atau objek yang tidak dapat diubah (seperti dalam kasus ini), kelas wrapper diperlukan.
Sunting: Sebenarnya, saya tidak menggunakan kelas anonim sama sekali, tetapi subkelas yang tepat:
sumber
Anda tidak dapat merujuk ke variabel non-final karena Spesifikasi Bahasa Jawa mengatakan demikian. Dari 8.1.3:
"Setiap variabel lokal, parameter metode formal atau parameter penangan pengecualian yang digunakan tetapi tidak dideklarasikan di kelas dalam harus dinyatakan final." Paragraf utuh.
Saya hanya dapat melihat bagian dari kode Anda - menurut saya penjadwalan modifikasi variabel lokal adalah ide yang aneh. Variabel lokal tidak ada lagi ketika Anda meninggalkan fungsi. Mungkin bidang statis suatu kelas akan lebih baik?
sumber
Saya hanya menulis sesuatu untuk menangani sesuatu sesuai dengan maksud penulis . Saya menemukan hal terbaik untuk dilakukan adalah membiarkan konstruktor mengambil semua objek dan kemudian dalam metode yang Anda terapkan gunakan objek konstruktor.
Namun, jika Anda menulis kelas antarmuka generik, maka Anda harus melewati Object, atau lebih baik daftar Object. Ini bisa dilakukan oleh Object [] atau bahkan lebih baik, Object ... karena lebih mudah untuk dipanggil.
Lihat contoh saya tepat di bawah.
Silakan lihat posting ini tentang penutupan Java yang mendukung ini di luar kotak: http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html
Versi 1 mendukung lewat penutupan non-final dengan autocasting:
https://github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/ Closure.java
sumber
Jika Anda ingin mengubah nilai dalam pemanggilan metode di dalam kelas anonim, "nilai" itu sebenarnya a
Future
. Jadi, jika Anda menggunakan Jambu Biji, Anda bisa menulissumber
Salah satu solusi yang saya perhatikan tidak disebutkan (kecuali saya melewatkannya, jika saya perbaiki saya), adalah penggunaan variabel kelas. Berlari ke dalam masalah ini mencoba untuk menjalankan thread baru dalam metode:
new Thread(){ Do Something }
.Panggilan
doSomething()
dari berikut ini akan berhasil. Anda tidak harus mendeklarasikannyafinal
, hanya perlu mengubah ruang lingkup variabel sehingga tidak dikumpulkan sebelum innerclass. Ini kecuali jika tentu saja proses Anda sangat besar dan mengubah ruang lingkup dapat menciptakan semacam konflik. Saya tidak ingin membuat variabel saya final karena tidak final / konstan.sumber
Jika variabel harus final, tidak bisa maka Anda dapat menetapkan nilai variabel ke variabel lain dan membuat ITU final sehingga Anda dapat menggunakannya sebagai gantinya.
sumber
gunakan ClassName.this.variableName untuk referensi variabel non-final
sumber
Anda bisa mendeklarasikan variabel di luar kelas luar. Setelah ini, Anda akan dapat mengedit variabel dari dalam kelas dalam. Saya kadang-kadang menghadapi masalah yang sama saat coding di android jadi saya mendeklarasikan variabel sebagai global dan berfungsi untuk saya.
sumber
Bisakah Anda membuat
lastPrice
,,priceObject
danprice
bidang kelas dalam anonim?sumber
Perhatian utama adalah apakah variabel di dalam instance kelas anonim dapat diselesaikan pada saat run-time. Bukan suatu keharusan untuk membuat variabel final selama dijamin bahwa variabel berada di dalam lingkup run-time. Sebagai contoh, silakan lihat dua variabel _statusMessage dan _statusTextView di dalam metode updateStatus ().
sumber
Apa yang berhasil bagi saya hanyalah mendefinisikan variabel di luar fungsi ini dari Anda.
Tepat sebelum fungsi utama menyatakan yaitu
sumber
Deklarasikan variabel sebagai statis dan referensi dalam metode yang diperlukan menggunakan className.variable
sumber
Non-static parameter cannot be referenced from a static context
Hanya penjelasan lain. Perhatikan contoh di bawah ini
Di sini Output akan
m1 Selesai
Utas t running
Utas t running
Utas t running
................
Sekarang metode m1 () selesai dan kami menetapkan variabel referensi o ke nol, Sekarang Obyek Kelas Luar memenuhi syarat untuk GC tetapi Objek Kelas Batin masih ada yang memiliki (Has-A) hubungan dengan objek Thread yang sedang berjalan. Tanpa objek kelas luar yang ada tidak ada kemungkinan metode m1 () yang ada dan tanpa metode m1 () yang ada tidak ada peluang ada variabel lokalnya tetapi jika objek kelas batin menggunakan variabel lokal metode m1 () maka semuanya jelas. .
Untuk menyelesaikan ini kita harus membuat salinan variabel lokal dan kemudian harus menyalin kemudian ke heap dengan objek kelas batin, apa yang dilakukan java hanya untuk variabel akhir karena mereka sebenarnya bukan variabel mereka seperti konstanta (Semuanya terjadi pada waktu kompilasi saja tidak saat runtime).
sumber
Untuk mengatasi masalah di atas, bahasa yang berbeda membuat keputusan yang berbeda.
untuk Java, solusinya adalah seperti yang kita lihat di artikel ini.
untuk C #, solusinya adalah memungkinkan efek samping dan menangkap dengan referensi adalah satu-satunya pilihan.
untuk C ++ 11, solusinya adalah memungkinkan programmer membuat keputusan. Mereka dapat memilih untuk menangkap berdasarkan nilai atau referensi. Jika menangkap berdasarkan nilai, tidak ada efek samping yang akan terjadi karena variabel yang dirujuk sebenarnya berbeda. Jika ditangkap dengan referensi, efek samping dapat terjadi tetapi programmer harus menyadarinya.
sumber
Karena membingungkan jika variabelnya tidak final, karena perubahan itu tidak akan diambil di kelas anonim.
Jadikan variabel sebagai 'harga' dan 'harga terakhir'.
- Edit
Ups, dan Anda juga harus tidak menetapkannya, jelas, dalam fungsi Anda. Anda akan memerlukan variabel lokal baru. Bagaimanapun, saya menduga seseorang telah memberi Anda jawaban yang lebih baik sekarang.
sumber