Latar Belakang
Ada tiga tempat di mana final dapat muncul di Jawa:
Membuat final kelas mencegah semua subklasifikasi kelas. Membuat metode akhir mencegah subclass dari metode menimpanya. Membuat field final mencegahnya agar tidak diubah nanti.
Kesalahpahaman
Ada optimasi yang terjadi di sekitar metode dan bidang akhir.
Metode terakhir memudahkan HotSpot untuk mengoptimalkan melalui inlining. Namun, HotSpot melakukan ini bahkan jika metode ini tidak final karena berfungsi dengan asumsi bahwa itu belum ditimpa sampai dibuktikan sebaliknya. Lebih lanjut tentang ini di SO
Variabel terakhir dapat dioptimalkan secara agresif, dan lebih lanjut tentang itu dapat dibaca di bagian JLS 17.5.3 .
Namun, dengan pemahaman itu orang harus sadar bahwa tak satu pun dari optimasi ini adalah tentang membuat kelas final. Tidak ada perolehan kinerja dengan membuat final kelas.
Aspek terakhir dari suatu kelas tidak ada hubungannya dengan ketidakberubahan juga. Seseorang dapat memiliki kelas yang tidak dapat diubah (seperti BigInteger ) yang tidak final, atau kelas yang bisa berubah dan final (seperti StringBuilder ). Keputusan tentang apakah kelas harus final adalah masalah desain.
Desain Akhir
String adalah salah satu tipe data yang paling banyak digunakan. Mereka ditemukan sebagai kunci untuk peta, mereka menyimpan nama pengguna dan kata sandi, itu adalah apa yang Anda baca dari keyboard atau bidang pada halaman web. String ada di mana - mana .
Peta
Hal pertama yang perlu dipertimbangkan tentang apa yang akan terjadi jika Anda dapat mensubktrasikan String adalah menyadari bahwa seseorang dapat membuat kelas String yang bisa berubah yang akan tampak seperti String. Ini akan mengacaukan Peta di mana-mana.
Pertimbangkan kode hipotetis ini:
Map t = new TreeMap<String, Integer>();
Map h = new HashMap<String, Integer>();
MyString one = new MyString("one");
MyString two = new MyString("two");
t.put(one, 1); h.put(one, 1);
t.put(two, 2); h.put(two, 2);
one.prepend("z");
Ini adalah masalah dengan menggunakan kunci yang bisa berubah-ubah secara umum dengan Peta, tetapi hal yang saya coba dapatkan di sana adalah bahwa tiba-tiba sejumlah hal tentang istirahat Peta. Entri tidak lagi berada di tempat yang tepat di peta. Dalam HashMap, nilai hash telah (seharusnya) berubah dan dengan demikian tidak lagi berada di entri yang benar. Di TreeMap, pohon sekarang rusak karena salah satu node berada di sisi yang salah.
Karena menggunakan String untuk kunci-kunci ini sangat umum, perilaku ini harus dicegah dengan membuat String menjadi final.
Anda mungkin tertarik membaca Mengapa String tidak dapat diubah di Jawa? untuk lebih lanjut tentang sifat abadi String.
String jahat
Ada sejumlah opsi jahat untuk Strings. Pertimbangkan jika saya membuat sebuah String yang selalu mengembalikan nilai true ketika equal dipanggil ... dan meneruskannya ke pemeriksaan kata sandi? Atau membuatnya agar penugasan ke MyString akan mengirim salinan String ke beberapa alamat email?
Ini adalah kemungkinan yang sangat nyata ketika Anda memiliki kemampuan untuk membuat subkelas String.
Optimalisasi Java.lang String
Sementara sebelumnya saya menyebutkan bahwa final tidak membuat String lebih cepat. Namun, kelas String (dan kelas-kelas lain di java.lang
) sering menggunakan perlindungan tingkat paket dari bidang dan metode untuk memungkinkan java.lang
kelas - kelas lain dapat bermain-main dengan internal daripada melalui API publik untuk String sepanjang waktu. Fungsi seperti getChars tanpa pengecekan rentang atau lastIndexOf yang digunakan oleh StringBuffer, atau konstruktor yang berbagi array yang mendasarinya (perhatikan bahwa Java 6 merupakan hal yang diubah karena masalah memori).
Jika seseorang membuat subkelas String, itu tidak akan dapat membagikan optimasi tersebut (kecuali jika itu bagian dari java.lang
juga, tapi itu paket yang disegel ).
Lebih sulit untuk merancang sesuatu untuk ekstensi
Merancang sesuatu yang bisa diperpanjang itu sulit . Ini berarti bahwa Anda harus mengekspos bagian internal Anda untuk hal lain agar dapat dimodifikasi.
String yang dapat diperpanjang tidak dapat memiliki kebocoran ingatannya diperbaiki. Bagian-bagian itu perlu terkena subclass dan mengubah kode itu berarti subclass akan rusak.
Java bangga dengan kompatibilitas mundur dan dengan membuka kelas inti untuk ekstensi, seseorang kehilangan beberapa kemampuan untuk memperbaiki hal-hal sambil tetap mempertahankan kemampuan komputasi dengan subkelas pihak ketiga.
Checkstyle memiliki aturan yang diberlakukan (yang benar-benar membuat saya frustrasi ketika menulis kode internal) yang disebut "DesignForExtension" yang memberlakukan bahwa setiap kelas adalah:
- Abstrak
- Terakhir
- Implementasi kosong
Yang rasional untuk itu adalah:
Gaya desain API ini melindungi kacamata super agar tidak rusak oleh subclass. The downside adalah bahwa subclass terbatas dalam fleksibilitas mereka, khususnya mereka tidak dapat mencegah pelaksanaan kode di superclass, tetapi itu juga berarti bahwa subclass tidak dapat merusak keadaan superclass dengan lupa memanggil metode super.
Mengizinkan perluasan kelas implementasi berarti bahwa subclass mungkin dapat merusak keadaan kelas yang menjadi dasarnya dan membuatnya sehingga berbagai jaminan yang diberikan superclass tidak valid. Untuk sesuatu yang kompleks seperti String, hampir pasti bahwa mengubah bagian itu akan merusak sesuatu.
Pengembang hurbis
Itu bagian dari menjadi pengembang. Pertimbangkan kemungkinan bahwa setiap pengembang akan membuat subclass String mereka sendiri dengan koleksi utilities mereka sendiri di dalamnya. Tetapi sekarang subkelas ini tidak dapat secara bebas ditugaskan kembali satu sama lain.
WleaoString foo = new WleaoString("foo");
MichaelTString bar = foo; // This doesn't work.
Cara ini menyebabkan kegilaan. Casting ke String di semua tempat dan memeriksa untuk melihat apakah String adalah turunan dari kelas String Anda atau jika tidak, membuat String baru berdasarkan yang itu dan ... hanya, tidak. Jangan.
Saya yakin Anda dapat menulis kelas String yang baik ... tetapi menulis beberapa implementasi string kepada orang-orang gila yang menulis C ++ dan harus berurusan dengan std::string
dan char*
dan sesuatu dari boost dan SString , dan yang lainnya .
Java String magic
Ada beberapa hal ajaib yang dilakukan Java dengan String. Ini membuatnya lebih mudah bagi seorang programmer untuk berurusan dengan tetapi memperkenalkan beberapa inkonsistensi dalam bahasa. Mengizinkan subclass pada String akan mengambil beberapa pemikiran yang sangat signifikan tentang bagaimana menangani hal-hal ajaib ini:
String Literals (JLS 3.10.5 )
Memiliki kode yang memungkinkan seseorang untuk melakukan:
String foo = "foo";
Ini tidak harus bingung dengan tinju dari jenis numerik seperti Integer. Anda tidak dapat melakukannya 1.toString()
, tetapi Anda dapat melakukannya "foo".concat(bar)
.
The +
operator (JLS 15.18.1 )
Tidak ada jenis referensi lain di Jawa yang memungkinkan operator untuk menggunakannya. String itu spesial. Operator concatenation string juga bekerja pada level kompiler sehingga "foo" + "bar"
menjadi "foobar"
saat dikompilasi daripada saat runtime.
Konversi String (JLS 5.1.11 )
Semua objek dapat dikonversi menjadi String hanya dengan menggunakannya dalam konteks String.
String Interning ( JavaDoc )
Kelas String memiliki akses ke kumpulan String yang memungkinkannya untuk memiliki representasi kanonik objek yang dihuni pada tipe kompilasi dengan String literal.
Mengizinkan subkelas String akan berarti bahwa bit-bit ini dengan String yang membuatnya lebih mudah diprogram akan menjadi sangat sulit atau tidak mungkin dilakukan ketika jenis-jenis String lainnya dimungkinkan.