Mengapa saya harus menggunakan kata kunci "final" pada parameter metode di Jawa?

381

Saya tidak dapat memahami di mana finalkata 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;
}
Basil Bourque
sumber
101
Satu titik cepat pada terminologi - Java tidak memiliki pass-by-reference sama sekali. Ini memiliki referensi lewat nilai yang bukan hal yang sama. Dengan semantik referensi sebenarnya, hasil kode Anda akan berbeda.
Jon Skeet
6
Apa perbedaan antara pass by reference dan pass reference by value?
NobleUplift
Lebih mudah untuk menggambarkan perbedaan itu dalam konteks C (setidaknya untuk saya). Jika saya meneruskan sebuah pointer ke metode seperti: <code> int foo (int bar) </code>, maka pointer itu diteruskan oleh nilai. Berarti ini disalin, jadi jika saya melakukan sesuatu di dalam metode itu seperti <code> gratis (bar); bar = malloc (...); </code> maka saya baru saja melakukan hal yang sangat buruk. Panggilan gratis sebenarnya akan membebaskan potongan memori yang diarahkan (jadi pointer apa pun yang saya lewati sekarang menggantung). Namun, <code> int foo (int & bar) </bar> berarti kode itu valid dan nilai pointer yang diteruskan akan diubah.
jerslan
1
Yang pertama seharusnya int foo(int* bar)dan yang terakhir int foo(int* &bar). Yang terakhir melewati pointer dengan referensi, yang pertama melewati referensi dengan nilai.
jerslan
2
@ Martin, menurut saya, ini pertanyaan yang bagus ; lihat judul untuk pertanyaan, dan konten posting sebagai penjelasan mengapa pertanyaan diajukan. Mungkin saya salah paham aturan di sini, tapi ini persis pertanyaan yang saya inginkan ketika mencari "penggunaan parameter akhir dalam metode" .
Victor Zamanian

Jawaban:

239

Hentikan Penugasan Kembali Variabel

Meskipun jawaban ini menarik secara intelektual, saya belum membaca jawaban singkatnya yang sederhana:

Gunakan kata kunci akhir ketika Anda ingin kompiler untuk mencegah variabel ditugaskan kembali ke objek yang berbeda.

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.

// Example use of this method: 
//   this.doSomething( "tiger" );
void doSomething( String arg ) {
  String x = arg;   // Both variables now point to the same String object.
  x = "elephant";   // This variable now points to a different String object.
  arg = "giraffe";  // Ditto. Now neither variable points to the original passed String.
}

Tandai variabel lokal sebagai final . Ini menghasilkan kesalahan kompilator.

void doSomething( String arg ) {
  final String x = arg;  // Mark variable as 'final'.
  x = "elephant";  // Compiler error: The final local variable x cannot be assigned. 
  arg = "giraffe";  
}

Sebagai gantinya, mari tandai variabel parameter sebagai final . Ini juga menghasilkan kesalahan kompiler.

void doSomething( final String arg ) {  // Mark argument as 'final'.
  String x = arg;   
  x = "elephant"; 
  arg = "giraffe";  // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}

Pesan moral dalam cerita:

Jika Anda ingin memastikan variabel selalu menunjuk ke objek yang sama, tandai variabel final .

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 barisarg = . 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 finalke semua argumen?

Haruskah kita menambahkan finalsetiap parameter metode yang dideklarasikan?

  • Secara teori, ya.
  • Dalam praktiknya, tidak.
    ➥ Tambahkan finalhanya 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 finalmasing-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 menambahkan finalhanya dalam kode yang lebih panjang atau lebih terlibat di mana argumen mungkin keliru untuk variabel lokal atau anggota.

Kasus lain ditambahkan untuk kelengkapan

public class MyClass {
    private int x;
    //getters and setters
}

void doSomething( final MyClass arg ) {  // Mark argument as 'final'.

   arg =  new MyClass();  // Compiler error: The passed argument variable arg  cannot be re-assigned to another object.

   arg.setX(20); // allowed
  // We can re-assign properties of argument which is marked as final
 }
Basil Bourque
sumber
23
"Sebagai praktik pemrograman yang baik (dalam bahasa apa pun), Anda tidak boleh menetapkan ulang variabel parameter / argumen [..]" Maaf saya benar-benar harus memanggil Anda untuk yang ini. Argumen penugasan ulang adalah praktik standar dalam bahasa seperti Javascript, di mana jumlah argumen yang disahkan (atau bahkan jika ada yang disahkan) tidak ditentukan oleh metode tanda tangan. Misalnya diberi tanda tangan seperti: "function say (msg)" orang akan memastikan argumen 'msg' ditugaskan, seperti: "msg = msg || 'Hello World!';". Programmer Javascript terbaik di dunia melanggar praktik terbaik Anda. Baca saja sumber jQuery.
Stijn de Witt
37
@StijndeWitt Contoh Anda menunjukkan masalah saat menetapkan ulang variabel argumen. Anda kehilangan informasi tanpa imbalan apa pun sebagai imbalannya: (a) Anda telah kehilangan nilai asli yang diteruskan, (b) Anda telah kehilangan niat metode panggilan (Apakah penelepon lulus 'Hello World!' Atau apakah kami default). A & b keduanya berguna untuk pengujian, kode panjang, dan saat nilainya diubah lebih lanjut nanti. Saya mendukung pernyataan saya: arg vars seharusnya tidak pernah ditugaskan kembali. Kode Anda harus: message = ( msg || 'Hello World"' ). Tidak ada alasan untuk tidak menggunakan var terpisah. Satu-satunya biaya adalah beberapa byte memori.
Basil Bourque
9
@ Basil: Ini lebih banyak kode (dalam byte) dan dalam Javascript yang dihitung. Sangat berat. Seperti banyak hal lain, itu berdasarkan pendapat. Sangat mungkin untuk sepenuhnya mengabaikan praktik pemrograman ini dan masih menulis kode yang sangat baik. Praktek pemrograman satu orang tidak menjadikannya praktik semua orang. Bersiaplah untuk itu semua, saya memilih untuk menulisnya dengan cara berbeda. Apakah itu membuat saya seorang programmer yang buruk, atau kode saya kode yang buruk?
Stijn de Witt
14
Menggunakan message = ( msg || 'Hello World"' )risiko saya nantinya menggunakan secara tidak sengaja msg. 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 memulainya if (!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) .
Beni Cherniavsky-Paskin
6
@ BeniCherniavsky-Paskin risiko yang Anda gambarkan hanya karena kesamaan antara messagedan msg. Tetapi jika dia akan menyebutnya sesuatu seperti processedMsgatau sesuatu yang lain yang memberikan konteks tambahan - kemungkinan kesalahan jauh lebih rendah. Fokus pada apa yang dia katakan bukan pada "bagaimana" dia mengatakannya. ;)
alfasin
230

Terkadang menyenangkan untuk eksplisit (agar mudah dibaca) bahwa variabel tidak berubah. Berikut adalah contoh sederhana di mana menggunakan finaldapat menyimpan beberapa kemungkinan sakit kepala:

public void setTest(String test) {
    test = test;
}

Jika Anda lupa kata kunci 'ini' pada setter, maka variabel yang ingin Anda atur tidak disetel. Namun, jika Anda menggunakan finalkata kunci pada parameter, maka bug akan ditangkap pada waktu kompilasi.

Jerry Sha
sumber
65
btw Anda akan melihat peringatan "Tugas untuk uji variabel tidak berpengaruh"
AvrDragon
12
@ AvrDragon Tapi, kita mungkin mengabaikan peringatan itu juga. Jadi, selalu lebih baik untuk memiliki sesuatu yang akan menghentikan kita dari melangkah lebih jauh, seperti kesalahan kompilasi, yang akan kita dapatkan dengan menggunakan kata kunci terakhir.
Sumit Desai
10
@ AvrDragon Itu tergantung pada lingkungan pengembangan. Anda seharusnya tidak mengandalkan IDE untuk menangkap hal-hal seperti ini untuk Anda, kecuali jika Anda ingin mengembangkan kebiasaan buruk.
b1nary.atr0phy
25
@ b1naryatr0phy sebenarnya ini adalah peringatan kompiler, bukan hanya IDE-tip
AvrDragon
8
@SumitDesai "Tapi, kita mungkin mengabaikan peringatan itu juga. Jadi, selalu lebih baik untuk memiliki sesuatu yang akan menghentikan kita dari melangkah lebih jauh, seperti kesalahan kompilasi, yang akan kita dapatkan dengan menggunakan kata kunci terakhir." Saya mengambil poin Anda tetapi ini adalah pernyataan yang sangat kuat yang saya pikir banyak pengembang Java akan tidak setuju. Peringatan kompiler ada karena suatu alasan dan pengembang yang kompeten tidak perlu kesalahan untuk 'memaksa' mereka untuk mempertimbangkan implikasinya.
Stuart Rossiter
127

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 finalvariabel 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 finalmemiliki 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?"

Jon Skeet
sumber
17
Tetapi untuk memparafrasekan Krusty dengan bahasa Denmark-nya, apa yang telah mereka lakukan untuk kita LATELY ? =)
James Schek
Yuval. Itu lucu! Saya kira perdamaian bisa terjadi bahkan jika itu dipaksakan oleh pedang!
gonzobrains
1
Pertanyaannya tampaknya lebih mirip dengan bertanya, "Apa yang belum dilakukan orang Romawi untuk kita?", Karena itu lebih merupakan kritik terhadap apa yang tidak dilakukan kata kunci akhir .
NobleUplift
"Apakah Anda menyarankan ada sejumlah besar pengembang yang menyarankan bahwa final lebih berpengaruh daripada yang sebenarnya?" Bagi saya itu adalah masalah utama: Saya sangat curiga sebagian besar pengembang menggunakannya berpikir bahwa hal itu memberlakukan kekekalan dari item yang diteruskan penelepon ketika tidak. Tentu saja, seseorang kemudian terlibat dalam perdebatan tentang apakah standar pengkodean harus 'melindungi terhadap' kesalahpahaman konseptual (yang harus disadari oleh pengembang 'kompeten') atau tidak (dan ini kemudian mengarah pada pendapat di luar lingkup SO) -jenis pertanyaan)!
Stuart Rossiter
1
@SarthakMittal: Nilai tidak akan disalin kecuali Anda benar-benar menggunakannya, jika itu yang Anda pikirkan.
Jon Skeet
76

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:

public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
    return new Iterator<Integer>(){
        int index = from;
        public Integer next()
        {
            return index++;
        }
        public boolean hasNext()
        {
            return index <= to;
        }
        // remove method omitted
    };
}

Di sini parameter fromdan toharus 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.

Michael Borgwardt
sumber
1
Anda kehilangan saya dari "Tetapi jika mereka belum final ...." Dapatkah Anda mencoba untuk mengulanginya, Mungkin saya belum punya cukup kopi.
hhafez
1
Anda memiliki variabel lokal dari - pertanyaannya adalah apa yang terjadi jika Anda menggunakan instance kelas anon di dalam metode dan itu mengubah nilai dari - orang akan mengharapkan perubahan terlihat dalam metode, karena mereka hanya melihat satu variabel. Untuk menghindari kebingungan ini, itu harus final.
Michael Borgwardt
Itu tidak membuat salinan, itu hanya referensi ke objek apa pun yang dirujuk.
vickirk
1
@vickirk: yakin itu membuat salinan - referensi, dalam hal jenis referensi.
Michael Borgwardt
Btw dengan asumsi kita tidak memiliki kelas anonim yang mereferensikan variabel-variabel itu, apakah Anda sadar jika ada perbedaan antara finalparameter fungsi dan parameter fungsi tidak final di mata HotSpot?
Pacerier
25

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.

Fortyrunner
sumber
4
Saya akan menyarankan ini juga, yang final menjadi default di versi masa depan dan bahwa Anda harus menentukan "bisa berubah" atau kata kunci yang lebih baik yang dikandung. Inilah artikel yang bagus tentang ini: lpar.ath0.com/2008/08/26/java-annoyance-final-parameters
Jeff Axelrod
Sudah lama, tetapi bisakah Anda memberikan contoh bug yang Anda tangkap?
user949300
Lihat jawaban terbanyak untuk jawaban. Itu memiliki contoh yang sangat bagus di mana variabel anggota tidak disetel dan sebagai gantinya parameter dimutasi.
Fortyrunner
18

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.

Jerman
sumber
2
Tepatnya, itu bukan bagian dari antarmuka fungsi, hanya implementasi. Ini membingungkan bahwa java memungkinkan (tetapi dirsregards) final pada parameter dalam antarmuka / deklarasi metode abstrak.
Beni Cherniavsky-Paskin
8

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.

starblue
sumber
Tidak yakin IDE dan referensi alat berlaku untuk posting OP atau topik. Yaitu "final" adalah pemeriksaan waktu kompilasi bahwa referensi tidak diubah / dirampok. Selanjutnya, untuk benar-benar menegakkan hal-hal seperti itu lihat jawaban tentang tidak ada perlindungan bagi anggota anak dari referensi akhir. Ketika membangun API misalnya, menggunakan IDE atau alat tidak akan membantu pihak eksternal menggunakan / memperluas kode tersebut.
Darrell Teague
4

Karena Java mengeluarkan salinan argumen, saya merasa relevansinya finalagak terbatas. Saya kira kebiasaan tersebut berasal dari era C ++ di mana Anda dapat melarang konten referensi diubah dengan melakukan a const 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 hilangkan final(kecuali saya tidak ingin seseorang menimpa metode dan kelas saya). Mungkin aku hanya dev sekolah tua.

Lawrence
sumber
1

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.): -

private String getString(String A, int i, String B, String C)
{
    if (i > 0)
        A += B;

    if (i > 100)
        A += C;

    return A;
}

Bermain sebagai penasihat iblis, apa sebenarnya yang salah dengan melakukan ini?

Chris Milburn
sumber
Bersikap hati-hati untuk membedakan IDE dari JVM runtime. Apa pun yang dilakukan IDE tidak relevan ketika kode byte yang dikompilasi berjalan di server kecuali jika IDE menambahkan kode untuk melindungi terhadap penjarahan variabel anggota seperti cacat dalam kode ketika itu dimaksudkan bahwa variabel tidak boleh dipindahkan tetapi salah adalah - maka tujuan dari kata kunci terakhir.
Darrell Teague
1

Jawaban singkat: finalmembantu sedikit tetapi ... gunakan pemrograman defensif di sisi klien sebagai gantinya.

Memang, masalahnya finaladalah 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.

Darrell Teague
sumber
2
"masalah dengan final adalah bahwa ia hanya menegakkan referensi tidak berubah" - Tidak Benar, Java sendiri mencegahnya. Variabel yang dikirimkan ke suatu metode tidak dapat memiliki referensi diubah oleh metode itu.
Madbreak
Silakan teliti sebelum memposting ... stackoverflow.com/questions/40480/…
Darrell Teague
Sederhananya, jika memang benar bahwa referensi ke referensi tidak dapat diubah, tidak akan ada diskusi tentang penyalinan defensif, kekekalan, tidak perlu kata kunci akhir, dll.
Darrell Teague
Entah Anda salah paham, atau Anda salah. Jika saya meneruskan referensi objek ke metode, dan metode itu menugaskannya kembali, referensi asli tetap utuh untuk (saya) pemanggil ketika metode menyelesaikan eksekusi. Java benar-benar merupakan nilai tambah. Dan Anda berani untuk menyatakan bahwa saya tidak melakukan penelitian.
Madbreak
Downvoting karena op bertanya mengapa menggunakan final, dan Anda hanya memberikan satu, alasan yang salah.
Madbreak
0

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.

Michael Rutherfurd
sumber
-1

Tindak lanjuti dengan apa yang diposkan Michel. Saya sendiri membuat contoh lain untuk menjelaskannya. Saya harap ini bisa membantu.

public static void main(String[] args){
    MyParam myParam = thisIsWhy(new MyObj());
    myParam.setArgNewName();

    System.out.println(myParam.showObjName());
}

public static MyParam thisIsWhy(final MyObj obj){
    MyParam myParam = new MyParam() {
        @Override
        public void setArgNewName() {
            obj.name = "afterSet";
        }

        @Override
        public String showObjName(){
            return obj.name;
        }
    };

    return myParam;
}

public static class MyObj{
    String name = "beforeSet";
    public MyObj() {
    }
}

public abstract static class MyParam{
    public abstract void setArgNewName();
    public abstract String showObjName();
}

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:

  1. Kami sekarang memiliki titik [MyObj obj] tersembunyi ke [Memory A in heap] yang sekarang hidup di objek MyParam.
  2. Kami juga punya [MyObj obj] lain yang merupakan argumen untuk [Memory B in heap] yang sekarang hidup dalam metode ThisIsWhy.

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".

KunYu Tsai
sumber