Di Jawa, haruskah saya menggunakan "final" untuk parameter dan penduduk lokal meskipun saya tidak harus melakukannya?

105

Java memungkinkan penandaan variabel (bidang / lokal / parameter) sebagai final, untuk mencegah penugasan kembali ke dalamnya. Saya merasa sangat berguna dengan bidang, karena membantu saya dengan cepat melihat apakah beberapa atribut - atau seluruh kelas - dimaksudkan untuk tidak berubah.

Di sisi lain, saya merasa itu jauh kurang berguna dengan penduduk lokal dan parameter, dan biasanya saya menghindari menandai mereka finalbahkan jika mereka tidak akan pernah ditugaskan kembali (dengan pengecualian yang jelas ketika mereka perlu digunakan di kelas dalam) . Akhir-akhir ini, bagaimanapun, saya telah menemukan kode yang digunakan final kapan saja bisa, yang saya kira secara teknis memberikan lebih banyak informasi.

Tidak lagi yakin dengan gaya pemrograman saya, saya bertanya-tanya apa kelebihan dan kekurangan lain dari penerapan di finalmana saja, apa gaya industri yang paling umum, dan mengapa.

ek
sumber
@Amir pertanyaan gaya pengkodean sepertinya berada di sini lebih baik daripada pada SO, dan saya tidak dapat menemukan kebijakan apa pun di FAQ atau pada meta situs ini mengenai hal ini. Bisakah Anda mengarahkan saya?
Oak
1
@ Oak Ia mengatakan: "Masalah pemrograman khusus, algoritma perangkat lunak, pengkodean , tanyakan pada Stack Overflow"
Amir Rezaei
7
@ Amir Saya tidak setuju, saya tidak melihat bagaimana ini merupakan masalah pengkodean. Bagaimanapun debat ini bukan di sini, jadi saya telah membuka meta-topik tentang masalah ini .
Oak
3
@ amir ini benar-benar tentang topik untuk situs ini, karena ini adalah pertanyaan subjektif tentang pemrograman - silakan lihat / faq
Jeff Atwood
1
Re, variabel lokal: Compiler Java lama memperhatikan apakah Anda mendeklarasikan lokal finalatau tidak, dan dioptimalkan sesuai. Kompiler modern cukup pintar untuk mengetahuinya sendiri. Setidaknya pada variabel lokal, finalhanya untuk kepentingan pembaca manusia. Jika rutinitas Anda tidak terlalu rumit, maka kebanyakan pembaca manusia harus bisa mengetahuinya sendiri juga.
Solomon Slow

Jawaban:

67

Saya menggunakan finalcara yang sama seperti Anda. Bagi saya itu terlihat berlebihan pada variabel lokal dan parameter metode, dan itu tidak menyampaikan informasi tambahan yang berguna.

Satu hal penting adalah berusaha untuk menjaga metode saya pendek dan bersih , masing-masing melakukan satu tugas. Jadi variabel dan parameter lokal saya memiliki cakupan yang sangat terbatas, dan hanya digunakan untuk satu tujuan. Ini meminimalkan kemungkinan penugasan kembali secara tidak sengaja.

Selain itu, seperti yang Anda pasti tahu, finaltidak menjamin bahwa Anda tidak dapat mengubah nilai / keadaan variabel (tidak bernilai). Hanya bahwa Anda tidak dapat menetapkan kembali referensi ke objek yang pernah diinisialisasi. Dengan kata lain, ia bekerja dengan mulus hanya dengan variabel tipe primitif atau tidak berubah. Mempertimbangkan

final String s = "forever";
final int i = 1;
final Map<String, Integer> m = new HashMap<String, Integer>();

s = "never"; // compilation error!
i++; // compilation error!
m.put(s, i); // fine

Ini berarti bahwa dalam banyak kasus masih tidak membuatnya lebih mudah untuk memahami apa yang terjadi di dalam kode, dan kesalahpahaman ini sebenarnya dapat menyebabkan bug halus yang sulit dideteksi.

Péter Török
sumber
1
Mengenai pengeditan - Saya mengetahui semantik final, terima kasih :) tapi poin bagus tentang metode pendek & bersih - Saya kira jika metode ini cukup pendek sehingga jelas variabel tidak ditugaskan ulang, ada motif yang kurang untuk mempertimbangkan finalkata kunci.
Oak
Bukankah lebih bagus jika kita bisa memiliki parameter final & variabel lokal, dan masih berupa sintaks yang pendek dan bersih? programmers.stackexchange.com/questions/199783/…
oberlies
7
"Final tidak menjamin bahwa Anda tidak dapat mengubah nilai / keadaan variabel (tidak bernilai). Hanya bahwa Anda tidak dapat menetapkan kembali referensi ke objek yang pernah diinisialisasi." Nonprimitive = referensi (satu-satunya tipe di Jawa adalah tipe primitif dan tipe referensi). Nilai variabel tipe referensi adalah referensi. Oleh karena itu, Anda tidak dapat menetapkan kembali referensi = Anda tidak dapat mengubah nilai.
user102008
2
+1 untuk jebakan referensi / negara. Perhatikan bahwa variabel penandaan akhir mungkin diperlukan untuk membuat penutupan dalam aspek fungsional baru
Java8
Perbandingan Anda bias seperti yang ditunjukkan oleh @ user102008. Penugasan variabel tidak sama dengan pembaruan nilainya
ericn
64

Gaya dan pemikiran pemrograman Java Anda baik-baik saja - tidak perlu meragukan diri Anda di sana.

Di sisi lain, saya merasa itu jauh kurang berguna dengan penduduk setempat dan parameter, dan biasanya saya menghindari menandai mereka sebagai final bahkan jika mereka tidak akan pernah ditugaskan kembali (dengan pengecualian yang jelas ketika mereka perlu digunakan di kelas dalam ).

Inilah mengapa Anda harus menggunakan finalkata kunci. Anda menyatakan bahwa ANDA tahu itu tidak akan pernah ditugaskan kembali, tetapi tidak ada orang lain yang tahu itu. Menggunakan finalsegera melemahkan kode Anda yang sedikit lebih.

Jonathan Khoo
sumber
8
Jika metode Anda jelas dan hanya melakukan satu hal, pembaca juga akan tahu. Kata terakhir hanya membuat kode tidak terbaca. Dan jika itu tidak terbaca, ini jauh lebih ambigu
eddieferetro
5
@eddieferetro Tidak setuju. Maksud kata kunci finalmenyatakan, yang membuat kode lebih mudah dibaca. Juga, begitu Anda harus berurusan dengan kode dunia nyata, Anda akan melihat itu jarang asli dan jelas, dan secara bebas menambahkan finaldi mana pun Anda bisa akan membantu Anda menemukan bug dan memahami kode lama dengan lebih baik.
Andres F.
4
Apakah "niat" bahwa variabel tidak akan pernah berubah, dan bahwa kode Anda bergantung pada fakta itu ? Atau maksudnya "kebetulan saja bahwa variabel ini tidak pernah berubah jadi saya menandainya sudah final". Belakangan adalah suara yang tidak berguna dan mungkin berbahaya. Dan karena Anda tampaknya menganjurkan menandai semua penduduk setempat, Anda melakukan yang berikutnya. Besar -1.
user949300
2
finalmenyatakan niat, yang biasanya merupakan hal yang baik dalam sepotong kode, tetapi harganya harus ditambah dengan kekacauan visual. Seperti kebanyakan hal dalam pemrograman, ada untung-ruginya di sini: apakah mengungkapkan fakta bahwa variabel Anda hanya akan digunakan hanya satu kali sepadan dengan kata kerja tambahan?
christopheml
Kekacauan visual akan sangat berkurang saat menggunakan penyorotan sintaksis. Saya menandai penduduk lokal saya yang ditugaskan hanya sekali - dan bahwa saya ingin ditugaskan hanya sekali - dengan final. Itu mungkin sebagian besar penduduk setempat, tetapi jelas tidak semua . Bagi saya tidak ada "kebetulan tidak pernah berubah". Ada 2 jenis penduduk setempat yang jelas terpisah di dunia saya. Mereka yang tidak pernah seharusnya berubah lagi (final) dan yang harus berubah sebagai akibat dari apa yang dikerjakan oleh tugas (yang jumlahnya sangat sedikit, membuat fungsinya cukup mudah untuk dipikirkan).
blubberdiblub
31

Salah satu keuntungan menggunakan final/ constsedapat mungkin adalah mengurangi beban mental bagi pembaca kode Anda.

Dia dapat yakin, bahwa nilai / referensi tidak pernah diubah nanti. Jadi dia tidak perlu memperhatikan modifikasi untuk memahami perhitungannya.

Saya telah berubah pikiran mengenai hal ini setelah mempelajari bahasa pemrograman murni-fungsional. Boy, betapa meringankannya, jika Anda bisa mempercayai "variabel" untuk selalu memegang nilai awalnya.

Program Lenny
sumber
16
Ya, di Java finaltidak menjamin bahwa Anda tidak dapat mengubah nilai / status variabel (tidak bernilai). Hanya bahwa Anda tidak dapat menetapkan kembali referensi ke objek yang pernah diinisialisasi.
Péter Török
9
Saya tahu, itulah sebabnya saya membedakan antara nilai dan referensi. Konsep ini adalah yang paling berguna dalam konteks struktur data yang tidak dapat diubah dan / atau fungsionalitas murni.
LennyProgrammers
7
@ PéterTörök Segera membantu dengan tipe primitif, dan agak membantu dengan referensi ke objek yang bisa berubah-ubah (setidaknya Anda tahu Anda selalu berurusan dengan objek yang sama!). Ini sangat membantu ketika berurusan dengan kode yang dirancang agar tidak berubah dari bawah ke atas.
Andres F.
18

Saya menganggap finaldalam parameter metode dan variabel lokal menjadi noise kode. Deklarasi metode Java bisa sangat panjang (terutama dengan obat generik) - tidak perlu membuatnya lagi.

Jika unit test ditulis dengan benar, menugaskan ke parameter yang "berbahaya" akan diambil, jadi seharusnya tidak pernah benar - benar menjadi masalah. Kejelasan visual lebih penting daripada menghindari kemungkinan bug yang tidak diambil karena unit test Anda memiliki cakupan yang tidak memadai.

Alat-alat seperti FindBugs dan CheckStyle yang dapat dikonfigurasikan untuk memecah bangunan jika tugas dibuat untuk parameter atau variabel lokal, jika Anda sangat peduli tentang hal-hal seperti itu.

Tentu saja, jika Anda perlu membuatnya final, misalnya karena Anda menggunakan nilai dalam kelas anonim, maka tidak ada masalah - itu solusi paling sederhana yang paling bersih.

Terlepas dari efek yang jelas dari menambahkan kata kunci tambahan ke parameter Anda, dan dengan demikian IMHO menyamarkannya, menambahkan parameter akhir ke metode sering dapat membuat kode dalam tubuh metode menjadi kurang mudah dibaca, yang membuat kode lebih buruk - menjadi "baik", kode harus mudah dibaca dan sesederhana mungkin. Sebagai contoh buat-buat, katakan saya punya metode yang perlu bekerja kasus tidak sensitif.

Tanpa final:

public void doSomething(String input) {
    input = input.toLowerCase();
    // do a few things with input
}

Sederhana. Bersih. Semua orang tahu apa yang terjadi.

Sekarang dengan 'final', opsi 1:

public void doSomething(final String input) {
    final String lowercaseInput = input.toLowerCase();
    // do a few things with lowercaseInput
}

Sementara membuat parameter finalmenghentikan kode menambahkan kode lebih jauh ke bawah dari berpikir dia bekerja dengan nilai asli, ada risiko yang sama bahwa kode lebih lanjut dapat menggunakan inputalih-alih lowercaseInput, yang seharusnya tidak dan yang tidak dapat dilindungi, karena Anda bisa ' t membawanya keluar dari ruang lingkup (atau bahkan menetapkan nulluntuk inputjika itu bahkan akan membantu pula).

Dengan 'final', opsi 2:

public void doSomething(final String input) {
    // do a few things with input.toLowerCase()
}

Sekarang kami baru saja membuat lebih banyak noise kode dan memperkenalkan performa yang baik karena harus meminta toLowerCase()n kali.

Dengan 'final', opsi 3:

public void doSomething(final String input) {
    doSomethingPrivate(input.toLowerCase());
}

/** @throws IllegalArgumentException if input not all lower case */
private void doSomethingPrivate(final String input) {
    if (!input.equals(input.toLowerCase())) {
        throw new IllegalArgumentException("input not lowercase");
    }
    // do a few things with input
}

Bicara tentang kebisingan kode. Ini adalah kecelakaan kereta api. Kami punya metode baru, blok pengecualian yang diperlukan, karena kode lain mungkin salah memanggilnya. Lebih banyak unit test untuk mencakup pengecualian. Semua untuk menghindari satu garis sederhana, dan IMHO lebih disukai dan tidak berbahaya.

Ada juga masalah bahwa metode tidak boleh terlalu lama sehingga Anda tidak dapat dengan mudah menerimanya secara visual dan mengetahui sekilas bahwa penugasan ke parameter telah terjadi.

Saya pikir itu adalah praktik / gaya yang baik bahwa jika Anda menetapkan ke parameter Anda melakukannya setiap awal dalam metode, lebih disukai baris pertama atau langsung setelah pengecekan input dasar, secara efektif menggantikannya untuk seluruh metode, yang memiliki efek konsisten dalam metode. Pembaca tahu untuk mengharapkan penugasan apa pun menjadi jelas (dekat deklarasi tanda tangan) dan di tempat yang konsisten, yang sangat memitigasi masalah yang ingin dihindari dengan menambahkan tugas akhir. Sebenarnya saya jarang menetapkan parameter, tetapi jika saya melakukannya saya selalu melakukannya di bagian atas metode.


Perhatikan juga bahwa finalsebenarnya tidak melindungi Anda seperti itu mungkin pada awalnya tampak:

public void foo(final Date date) {
    date.setTime(0); 
    // code that uses date
}

final tidak sepenuhnya melindungi Anda kecuali tipe parameternya primitif atau tidak dapat diubah.

Bohemian
sumber
Dalam kasus terakhir, finalapakah memberikan jaminan parsial bahwa Anda berurusan dengan Datecontoh yang sama . Beberapa jaminan lebih baik daripada tidak sama sekali (jika ada, Anda memperdebatkan kelas yang tidak dapat diubah dari awal!). Bagaimanapun, dalam praktiknya banyak dari apa yang Anda katakan harus memengaruhi bahasa-bahasa default yang ada yang tidak dapat diubah tetapi tidak, yang berarti itu bukan masalah.
Andres F.
+1 untuk menunjukkan risiko "bahwa kode lebih jauh ke bawah mungkin menggunakan input" Namun, saya lebih suka parameter saya menjadi final, dengan kemungkinan untuk membuatnya tidak final untuk kasus-kasus seperti di atas. Hanya saja tidak layak untuk mengirim tanda tangan dengan kata kunci.
maaartinus
Saya menemukan titik yang salah digunakan inputdaripada lowercaseInputmoot. Meskipun secara teknis benar, kesalahan itu mudah dikenali begitu ia menyebabkan Anda bug, karena Anda dapat dengan jelas melihat perbedaan semantik dari 2 variabel yang dinamai secara terpisah pada titik penggunaannya (asalkan Anda memberi mereka nama baik). Bagi saya, itu lebih berbahaya untuk mengacaukan 2 penggunaan yang berbeda secara semantik dari nilai-nilai yang terpisah menjadi variabel yang sama dan oleh karena itu nama yang sama. Ini dapat membuat pelacakan bug lebih sulit, karena Anda harus membaca dan memahami semua kode sebelumnya yang entah bagaimana terkait dengan variabel 1.
blubberdiblub
7

Saya membiarkan eclipse diletakkan di finaldepan setiap variabel lokal, karena saya menganggapnya membuat program lebih mudah dibaca. Saya tidak membuatnya dengan parameter, karena saya ingin menyimpan daftar parameter sesingkat mungkin, idealnya harus sesuai dalam satu baris.

maaartinus
sumber
2
Juga, untuk parameter, Anda dapat membuat masalah kompiler peringatan atau kesalahan jika parameter ditugaskan.
Oberlies
3
Justru sebaliknya, saya merasa lebih sulit untuk membaca karena hanya mengacaukan kode.
Steve Kuo
3
@Steve Kuo: Ini hanya memungkinkan saya untuk dengan cepat menemukan semua variabel. tidak ada untung besar, tetapi bersama-sama dengan mencegah tugas yang tidak disengaja itu bernilai 6 karakter. Saya akan jauh lebih bahagia jika ada sesuatu seperti varuntuk menandai variabel non-final. YMMV.
maaartinus
1
@maaartinus Setuju var! Sayangnya itu bukan default di Java dan sekarang sudah terlambat untuk mengubah bahasa. Karena itu, saya bersedia menerima ketidaknyamanan menulis yang kecil final:)
Andres F.
1
@maaartinus Setuju. Saya menemukan bahwa sekitar 80-90% dari variabel (lokal) saya tidak perlu dimodifikasi setelah inisialisasi. Karenanya, 80-90% dari mereka memiliki finalkata kunci yang diawali, sedangkan hanya 10-20% akan membutuhkan var...
JimmyB