Saya tidak dapat memahami di mana final
kata kunci sangat berguna ketika digunakan pada parameter metode.
Jika kita mengecualikan penggunaan kelas anonim, keterbacaan dan deklarasi niat maka tampaknya hampir tidak berharga bagi saya.
Menegakkan bahwa beberapa data tetap konstan tidak sekuat kelihatannya.
Jika parameternya primitif maka tidak akan berpengaruh karena parameter dilewatkan ke metode sebagai nilai dan mengubahnya tidak akan memiliki efek di luar ruang lingkup.
Jika kita melewatkan parameter dengan referensi, maka referensi itu sendiri adalah variabel lokal dan jika referensi diubah dari dalam metode, itu tidak akan memiliki efek apa pun dari luar lingkup metode.
Perhatikan contoh uji sederhana di bawah ini. Tes ini lolos meskipun metode mengubah nilai referensi yang diberikan padanya, itu tidak berpengaruh.
public void testNullify() {
Collection<Integer> c = new ArrayList<Integer>();
nullify(c);
assertNotNull(c);
final Collection<Integer> c1 = c;
assertTrue(c1.equals(c));
change(c);
assertTrue(c1.equals(c));
}
private void change(Collection<Integer> c) {
c = new ArrayList<Integer>();
}
public void nullify(Collection<?> t) {
t = null;
}
sumber
int foo(int* bar)
dan yang terakhirint foo(int* &bar)
. Yang terakhir melewati pointer dengan referensi, yang pertama melewati referensi dengan nilai.Jawaban:
Hentikan Penugasan Kembali Variabel
Meskipun jawaban ini menarik secara intelektual, saya belum membaca jawaban singkatnya yang sederhana:
Apakah variabel tersebut adalah variabel statis, variabel anggota, variabel lokal, atau variabel argumen / parameter, efeknya sepenuhnya sama.
Contoh
Mari kita lihat efeknya dalam aksi.
Pertimbangkan metode sederhana ini, di mana kedua variabel ( arg dan x ) keduanya dapat ditugaskan ulang objek yang berbeda.
Tandai variabel lokal sebagai final . Ini menghasilkan kesalahan kompilator.
Sebagai gantinya, mari tandai variabel parameter sebagai final . Ini juga menghasilkan kesalahan kompiler.
Pesan moral dalam cerita:
Jangan Tetapkan Kembali Argumen
Sebagai praktik pemrograman yang baik (dalam bahasa apa pun), Anda tidak boleh menetapkan ulang variabel parameter / argumen ke objek selain objek yang diteruskan oleh metode pemanggilan. Dalam contoh di atas, seseorang seharusnya tidak pernah menulis baris
arg =
. Karena manusia membuat kesalahan, dan programmer adalah manusia, mari kita minta kompiler untuk membantu kita. Tandai setiap parameter / variabel argumen sebagai 'final' sehingga kompiler dapat menemukan dan menandai setiap penugasan ulang tersebut.Dalam retrospeksi
Sebagaimana dicatat dalam jawaban lain ... Dengan tujuan desain asli Java untuk membantu programmer menghindari kesalahan bodoh seperti membaca melewati akhir array, Java seharusnya dirancang untuk secara otomatis menegakkan semua variabel parameter / argumen sebagai 'final'. Dengan kata lain, argumen tidak boleh berupa variabel . Namun melihat ke belakang adalah 20/20 visi, dan desainer Java memiliki tangan penuh pada saat itu.
Jadi, selalu tambahkan
final
ke semua argumen?Haruskah kita menambahkan
final
setiap parameter metode yang dideklarasikan?➥ Tambahkan
final
hanya ketika kode metode panjang atau rumit, di mana argumen mungkin keliru untuk variabel lokal atau anggota dan mungkin ditugaskan ulang.Jika Anda setuju dengan praktik yang tidak pernah menetapkan ulang argumen, Anda akan cenderung untuk menambahkan argumen
final
masing-masing. Tapi ini membosankan dan membuat pernyataan ini sedikit sulit dibaca.Untuk kode pendek sederhana di mana argumen jelas merupakan argumen, dan bukan variabel lokal atau variabel anggota, saya tidak repot menambahkan
final
. Jika kodenya cukup jelas, tanpa ada kemungkinan saya atau programmer lain melakukan pemeliharaan atau refactoring secara tidak sengaja salah mengartikan variabel argumen sebagai sesuatu selain argumen, maka jangan repot-repot. Dalam pekerjaan saya sendiri, saya menambahkanfinal
hanya dalam kode yang lebih panjang atau lebih terlibat di mana argumen mungkin keliru untuk variabel lokal atau anggota.Kasus lain ditambahkan untuk kelengkapan
sumber
message = ( msg || 'Hello World"' )
. Tidak ada alasan untuk tidak menggunakan var terpisah. Satu-satunya biaya adalah beberapa byte memori.message = ( msg || 'Hello World"' )
risiko saya nantinya menggunakan secara tidak sengajamsg
. Ketika kontrak yang saya maksudkan adalah "perilaku tanpa arg / null / undefined tidak bisa dibedakan dari lewat"Hello World"
", itu adalah praktik pemrograman yang baik untuk berkomitmen padanya di awal fungsi. [Ini dapat dicapai tanpa penugasan kembali dengan memulainyaif (!msg) return myfunc("Hello World");
tetapi itu menjadi sulit dengan banyak argumen.] Dalam kasus yang jarang terjadi di mana logika dalam fungsi harus peduli apakah default digunakan, saya lebih suka menunjuk nilai sentinel khusus (lebih disukai publik) .message
danmsg
. Tetapi jika dia akan menyebutnya sesuatu sepertiprocessedMsg
atau sesuatu yang lain yang memberikan konteks tambahan - kemungkinan kesalahan jauh lebih rendah. Fokus pada apa yang dia katakan bukan pada "bagaimana" dia mengatakannya. ;)Terkadang menyenangkan untuk eksplisit (agar mudah dibaca) bahwa variabel tidak berubah. Berikut adalah contoh sederhana di mana menggunakan
final
dapat menyimpan beberapa kemungkinan sakit kepala:Jika Anda lupa kata kunci 'ini' pada setter, maka variabel yang ingin Anda atur tidak disetel. Namun, jika Anda menggunakan
final
kata kunci pada parameter, maka bug akan ditangkap pada waktu kompilasi.sumber
Ya, tidak termasuk kelas anonim, pembacaan dan deklarasi niat itu hampir tidak berharga. Apakah ketiga hal itu tidak berharga?
Secara pribadi saya cenderung tidak menggunakan
final
variabel lokal dan parameter kecuali saya menggunakan variabel dalam kelas batin anonim, tapi saya pasti bisa melihat titik mereka yang ingin menjelaskan bahwa nilai parameter itu sendiri tidak akan berubah (bahkan jika objek itu merujuk pada perubahan isinya). Bagi mereka yang merasa menambah keterbacaan, saya pikir itu sepenuhnya masuk akal untuk dilakukan.Titik Anda akan lebih penting jika ada yang benar-benar mengklaim bahwa itu tidak menyimpan data konstan dengan cara yang tidak - tapi saya tidak ingat melihat klaim tersebut. Apakah Anda menyarankan ada sejumlah besar pengembang yang menyarankan bahwa
final
memiliki efek lebih daripada yang sebenarnya?EDIT: Saya harus benar-benar menyimpulkan semua ini dengan referensi Monty Python; pertanyaannya agak mirip dengan bertanya, "Apa yang telah dilakukan orang Romawi bagi kita?"
sumber
Izinkan saya menjelaskan sedikit tentang satu kasus di mana Anda harus menggunakan final, yang telah disebutkan Jon:
Jika Anda membuat kelas dalam anonim dalam metode Anda dan menggunakan variabel lokal (seperti parameter metode) di dalam kelas itu, maka kompiler memaksa Anda untuk membuat parameter final:
Di sini parameter
from
danto
harus final sehingga dapat digunakan di dalam kelas anonim.Alasan untuk persyaratan itu adalah ini: Variabel lokal hidup di stack, oleh karena itu mereka hanya ada saat metode dijalankan. Namun, instance kelas anonim dikembalikan dari metode, sehingga dapat hidup lebih lama. Anda tidak dapat menyimpan tumpukan, karena diperlukan untuk pemanggilan metode selanjutnya.
Jadi yang dilakukan Java adalah meletakkan salinan variabel lokal tersebut sebagai variabel instance tersembunyi ke dalam kelas anonim (Anda dapat melihatnya jika Anda memeriksa kode byte). Tetapi jika mereka tidak final, yang satu mungkin mengharapkan kelas anonim dan metode melihat perubahan yang lain buat variabel. Untuk mempertahankan ilusi bahwa hanya ada satu variabel daripada dua salinan, itu harus final.
sumber
final
parameter fungsi dan parameter fungsi tidak final di mata HotSpot?Saya menggunakan final sepanjang waktu pada parameter.
Apakah itu menambah sebanyak itu? Tidak juga.
Apakah saya akan mematikannya? Tidak.
Alasannya: Saya menemukan 3 bug di mana orang telah menulis kode ceroboh dan gagal menetapkan variabel anggota di accessors. Semua bug terbukti sulit ditemukan.
Saya ingin melihat ini membuat default di versi Java yang akan datang. Hal lulus / nilai referensi naik banyak sekali programmer junior.
Satu hal lagi .. metode saya cenderung memiliki jumlah parameter yang rendah sehingga teks tambahan pada deklarasi metode tidak menjadi masalah.
sumber
Menggunakan final dalam parameter metode tidak ada hubungannya dengan apa yang terjadi pada argumen di sisi pemanggil. Ini hanya dimaksudkan untuk menandainya sebagai tidak berubah di dalam metode itu. Ketika saya mencoba mengadopsi gaya pemrograman yang lebih fungsional, saya agak melihat nilainya.
sumber
final
pada parameter dalam antarmuka / deklarasi metode abstrak.Secara pribadi saya tidak menggunakan final pada parameter metode, karena menambahkan terlalu banyak kekacauan pada daftar parameter. Saya lebih suka menerapkan bahwa parameter metode tidak diubah melalui sesuatu seperti Checkstyle.
Untuk variabel lokal saya menggunakan final bila memungkinkan, saya bahkan membiarkan Eclipse melakukan itu secara otomatis dalam pengaturan saya untuk proyek pribadi.
Saya pasti ingin sesuatu yang lebih kuat seperti C / C ++ const.
sumber
Karena Java mengeluarkan salinan argumen, saya merasa relevansinya
final
agak terbatas. Saya kira kebiasaan tersebut berasal dari era C ++ di mana Anda dapat melarang konten referensi diubah dengan melakukan aconst char const *
. Saya merasa hal semacam ini membuat Anda percaya bahwa pengembang pada dasarnya bodoh sebagai pelacur dan perlu dilindungi terhadap setiap karakter yang ia ketik. Dengan segala kerendahan hati boleh saya katakan, saya menulis sangat sedikit bug meskipun saya hilangkanfinal
(kecuali saya tidak ingin seseorang menimpa metode dan kelas saya). Mungkin aku hanya dev sekolah tua.sumber
Saya tidak pernah menggunakan final dalam daftar parameter, itu hanya menambah kekacauan seperti yang dikatakan responden sebelumnya. Juga di Eclipse Anda dapat mengatur penetapan parameter untuk menghasilkan kesalahan sehingga menggunakan final dalam daftar parameter tampaknya cukup berlebihan bagi saya. Menariknya ketika saya mengaktifkan pengaturan Eclipse untuk penugasan parameter menghasilkan kesalahan pada itu menangkap kode ini (ini hanya bagaimana saya ingat aliran, bukan kode yang sebenarnya.): -
Bermain sebagai penasihat iblis, apa sebenarnya yang salah dengan melakukan ini?
sumber
Jawaban singkat:
final
membantu sedikit tetapi ... gunakan pemrograman defensif di sisi klien sebagai gantinya.Memang, masalahnya
final
adalah bahwa itu hanya menegakkan referensi tidak berubah, dengan senang hati memungkinkan anggota objek yang dirujuk untuk dimutasi, tanpa diketahui penelepon. Oleh karena itu praktik terbaik dalam hal ini adalah pemrograman defensif di sisi penelepon, menciptakan contoh yang sangat abadi atau salinan objek yang dalam yang terancam dirampok oleh API yang tidak bermoral.sumber
Satu alasan tambahan untuk menambahkan final ke deklarasi parameter adalah membantu untuk mengidentifikasi variabel yang perlu diganti namanya sebagai bagian dari refactoring "Extract Method". Saya telah menemukan bahwa menambahkan final ke setiap parameter sebelum memulai metode refactoring besar dengan cepat memberitahu saya jika ada masalah yang perlu saya atasi sebelum melanjutkan.
Namun, saya biasanya menghapusnya sebagai berlebihan di akhir refactoring.
sumber
Tindak lanjuti dengan apa yang diposkan Michel. Saya sendiri membuat contoh lain untuk menjelaskannya. Saya harap ini bisa membantu.
Dari kode di atas, dalam metode thisIsWhy () , kita benar-benar tidak menetapkan satu [argumen MyObj obj] untuk referensi nyata di MyParam. Sebagai gantinya, kami hanya menggunakan [argumen MyObj obj] dalam metode di dalam MyParam.
Tetapi setelah kita menyelesaikan metode thisIsWhy () , haruskah argumen (objek) MyObj tetap ada?
Sepertinya sudah seharusnya, karena kita dapat melihat secara utama kita masih memanggil metode showObjName () dan itu perlu mencapai obj . MyParam masih akan menggunakan / mencapai argumen metode bahkan metode itu sudah kembali!
Bagaimana Java benar-benar mencapai ini adalah untuk menghasilkan salinan juga merupakan referensi tersembunyi dari argumen MyObj obj di dalam objek MyParam (tapi itu bukan bidang formal di MyParam sehingga kita tidak bisa melihatnya)
Seperti yang kita sebut "showObjName", ia akan menggunakan referensi itu untuk mendapatkan nilai yang sesuai.
Tetapi jika kita tidak menempatkan argumen final, yang menyebabkan situasi kita dapat menetapkan kembali memori (objek) baru untuk argumen MyObj obj .
Secara teknis tidak ada bentrokan sama sekali! Jika kami diizinkan untuk melakukan itu, di bawah ini adalah situasinya:
Tidak ada bentrokan, tapi "BINGUNG !!" Karena mereka semua menggunakan "nama referensi" yang sama yaitu "obj" .
Untuk menghindari ini, tetapkan sebagai "final" untuk menghindari programmer melakukan kode "rawan kesalahan".
sumber