Salah satu fitur Java 8 yang paling berguna adalah default
metode baru pada antarmuka. Pada dasarnya ada dua alasan (mungkin ada yang lain) mengapa mereka diperkenalkan:
- Memberikan implementasi standar aktual. Contoh:
Iterator.remove()
- Mengizinkan evolusi JDK API. Contoh:
Iterable.forEach()
Dari perspektif perancang API, saya ingin dapat menggunakan pengubah lain pada metode antarmuka, misalnya final
. Ini akan berguna ketika menambahkan metode kenyamanan, mencegah penggantian "tidak disengaja" dalam mengimplementasikan kelas:
interface Sender {
// Convenience method to send an empty message
default final void send() {
send(null);
}
// Implementations should only implement this method
void send(String message);
}
Di atas sudah merupakan praktik umum jika Sender
kelas:
abstract class Sender {
// Convenience method to send an empty message
final void send() {
send(null);
}
// Implementations should only implement this method
abstract void send(String message);
}
Sekarang, default
dan final
jelas-jelas bertentangan dengan kata kunci, tetapi kata kunci default itu sendiri tidak sepenuhnya diperlukan , jadi saya berasumsi bahwa kontradiksi ini disengaja, untuk mencerminkan perbedaan halus antara "metode kelas dengan tubuh" (hanya metode) dan "antarmuka" metode dengan tubuh " (metode default), yaitu perbedaan yang belum saya mengerti.
Pada beberapa titik waktu, dukungan untuk modifier seperti static
dan final
pada metode antarmuka belum sepenuhnya dieksplorasi, mengutip Brian Goetz :
Bagian lainnya adalah seberapa jauh kita akan pergi untuk mendukung alat-alat kelas-bangunan di antarmuka, seperti metode final, metode pribadi, metode yang dilindungi, metode statis, dll. Jawabannya adalah: kita belum tahu
Sejak saat itu di akhir 2011, jelas, dukungan untuk static
metode dalam antarmuka telah ditambahkan. Jelas, ini menambahkan banyak nilai ke perpustakaan JDK sendiri, seperti dengan Comparator.comparing()
.
Pertanyaan:
Apa alasannya final
(dan juga static final
) tidak pernah berhasil ke antarmuka Java 8?
sumber
final
mencegah metode ditimpa, dan melihat bagaimana Anda HARUS mengganti metode yang diwarisi dari antarmuka, saya tidak mengerti mengapa masuk akal untuk membuatnya final. Kecuali jika itu menandakan bahwa metode ini adalah final SETELAH menimpa itu sekali .. Dalam hal itu, mungkin ada kesulitan qas? Jika saya tidak memahami hak ini, izinkan saya kmow. Tampaknya menarikfinal
akan digunakan dalam mencegah implementasi kelas dari mengesampingkan implementasi standar metode antarmuka.Jawaban:
Pertanyaan ini, sampai taraf tertentu, terkait dengan Apa alasan mengapa “disinkronkan” tidak diperbolehkan dalam metode antarmuka Java 8?
Hal utama yang perlu dipahami tentang metode default adalah bahwa tujuan desain utama adalah evolusi antarmuka , bukan "mengubah antarmuka menjadi sifat (biasa-biasa saja)". Sementara ada beberapa tumpang tindih di antara keduanya, dan kami mencoba mengakomodasi dengan yang kedua di mana tidak menghalangi yang pertama, pertanyaan-pertanyaan ini paling baik dipahami jika dilihat dalam cahaya ini. (Catatan juga bahwa metode kelas yang akan berbeda dari metode antarmuka, tidak peduli apa maksud, berdasarkan fakta bahwa metode antarmuka dapat multiply diwariskan.)
Gagasan dasar dari metode default adalah: ini adalah metode antarmuka dengan implementasi default, dan kelas turunan dapat memberikan implementasi yang lebih spesifik. Dan karena pusat desain adalah evolusi antarmuka, itu adalah tujuan desain kritis bahwa metode default dapat ditambahkan ke antarmuka setelah fakta dengan cara yang kompatibel dengan sumber dan biner.
Jawaban terlalu sederhana untuk "mengapa metode final tidak final" adalah bahwa maka tubuh tidak hanya akan menjadi implementasi standar, itu akan menjadi satu-satunya implementasi. Meskipun itu adalah jawaban yang terlalu sederhana, itu memberi kita petunjuk bahwa pertanyaannya sudah menuju ke arah yang dipertanyakan.
Alasan lain mengapa metode antarmuka akhir dipertanyakan adalah bahwa mereka menciptakan masalah yang tidak mungkin untuk implementator. Misalnya, anggap Anda memiliki:
Di sini, semuanya baik;
C
mewarisifoo()
dariA
. Sekarang seandainyaB
diubah untuk memilikifoo
metode, dengan default:Sekarang, ketika kita pergi untuk mengkompilasi ulang
C
, kompiler akan memberi tahu kita bahwa ia tidak tahu perilaku apa yang diwarisifoo()
, jadiC
harus menimpanya (dan dapat memilih untuk mendelegasikanA.super.foo()
jika ingin mempertahankan perilaku yang sama.) Tetapi bagaimana jikaB
telah membuat default-nyafinal
, danA
tidak di bawah kendali penulisC
? SekarangC
sudah tak dapat diperbaiki lagi; itu tidak dapat dikompilasi tanpa mengesampingkanfoo()
, tetapi tidak bisa mengesampingkanfoo()
jika itu finalB
.Ini hanyalah salah satu contoh, tetapi intinya adalah bahwa finalitas untuk metode benar-benar alat yang lebih masuk akal di dunia kelas warisan tunggal (umumnya yang dipadukan dengan perilaku), daripada antarmuka yang hanya berkontribusi perilaku dan dapat digandakan mewarisi. Terlalu sulit untuk berpikir tentang "apa antarmuka lain yang mungkin dicampur ke dalam implementator akhirnya", dan membiarkan metode antarmuka menjadi final kemungkinan akan menyebabkan masalah ini (dan mereka akan meledak bukan pada orang yang menulis antarmuka, tetapi pada pengguna miskin yang mencoba mengimplementasikannya.)
Alasan lain untuk melarang mereka adalah bahwa mereka tidak akan berarti apa yang Anda pikir mereka maksudkan. Implementasi default hanya dipertimbangkan jika kelas (atau superclasses) tidak memberikan deklarasi (konkret atau abstrak) dari metode ini. Jika metode default adalah final, tetapi superclass sudah mengimplementasikan metode tersebut, defaultnya akan diabaikan, yang mungkin bukan yang diharapkan oleh pembuat default ketika menyatakannya final. (Perilaku pewarisan ini merupakan refleksi dari pusat desain untuk metode default - evolusi antarmuka. Ini harus dimungkinkan untuk menambahkan metode default (atau implementasi default ke metode antarmuka yang ada) ke antarmuka yang ada yang sudah memiliki implementasi, tanpa mengubah perilaku kelas yang ada yang mengimplementasikan antarmuka,
sumber
Di milis lambda ada banyak diskusi tentang hal itu . Salah satu yang tampaknya mengandung banyak diskusi tentang semua hal itu adalah sebagai berikut: Visibilitas metode antarmuka Bervariasi (adalah Pembela Final) .
Dalam diskusi ini, Talden, penulis pertanyaan asli menanyakan sesuatu yang sangat mirip dengan pertanyaan Anda:
Akhirnya jawaban Brian Goetz adalah:
Jadi, kemungkinan besar itu tidak pernah dilaksanakan karena tidak pernah menjadi bagian dari ruang lingkup. Itu tidak pernah diusulkan pada waktunya untuk dipertimbangkan.
Dalam diskusi hangat lainnya tentang metode bek terakhir pada subjek, Brian berkata lagi :
Jadi ini memperkuat teori saya bahwa itu bukan bagian dari ruang lingkup atau bagian dari desain mereka. Apa yang mereka lakukan adalah menyediakan fungsionalitas yang cukup untuk menangani masalah evolusi API.
sumber
Akan sulit untuk menemukan dan mengidentifikasi jawaban "THE", karena resolusi yang disebutkan dalam komentar dari @EJP: Ada sekitar 2 (+/- 2) orang di dunia yang dapat memberikan jawaban yang pasti sama sekali . Dan dalam keraguan, jawabannya mungkin hanya sesuatu seperti "Mendukung metode standar akhir tampaknya tidak sebanding dengan upaya restrukturisasi mekanisme resolusi panggilan internal". Ini spekulasi, tentu saja, tetapi setidaknya didukung oleh bukti-bukti halus, seperti Pernyataan ini (oleh salah satu dari dua orang) di milis OpenJDK :
dan fakta-fakta sepele seperti itu suatu metode sama sekali tidak dianggap sebagai metode final (benar-benar) ketika itu adalah
default
metode, seperti yang saat ini diimplementasikan dalam metode Method :: is_final_method di OpenJDK.Lebih jauh lagi, informasi "otoritatif" memang sulit ditemukan, bahkan dengan pencarian web yang berlebihan dan dengan membaca log komit. Saya berpikir bahwa itu mungkin terkait dengan ambiguitas potensial selama resolusi panggilan metode antarmuka dengan
invokeinterface
instruksi dan dan metode panggilan kelas, sesuai denganinvokevirtual
instruksi: Untukinvokevirtual
instruksi, mungkin ada pencarian vtable sederhana , karena metode tersebut harus diwariskan dari superclass, atau diimplementasikan oleh kelas secara langsung. Berbeda dengan itu,invokeinterface
panggilan harus memeriksa situs panggilan masing-masing untuk mengetahui antarmuka mana panggilan ini sebenarnya mengacu (ini dijelaskan secara lebih rinci di halaman InterfaceCalls dari Wiki HotSpot). Namun,final
metode tidak dimasukkan sama sekali ke dalam vtable , atau mengganti entri yang ada di vtable (lihat klassVtable.cpp. Line 333 ), dan demikian pula, metode default mengganti entri yang ada di vtable (lihat klassVtable.cpp, Line 202 ) . Jadi alasan sebenarnya (dan dengan demikian, jawabannya) harus disembunyikan lebih dalam di dalam mekanisme resolusi pemanggilan metode (agak rumit), tetapi mungkin referensi ini akan tetap dianggap membantu, baik hanya bagi orang lain yang berhasil memperoleh jawaban aktual dari itu.sumber
int
, lihat stackoverflow.com/questions/430346 ... ...synchronized
padadefault
metode.final
pertanyaan ini , dan agak merendahkan bahwa tidak ada orang lain yang memikirkan contoh serupa (atau, mungkin, beberapa orang memikirkan contoh ini, tetapi tidak merasa cukup otoritatif untuk menjawab). Jadi sekarang (maaf, saya harus melakukan ini:) akhir kata diucapkan.Saya tidak akan berpikir itu perlu untuk menentukan
final
pada metode antarmuka convienience, saya bisa setuju meskipun itu mungkin membantu, tetapi tampaknya biaya telah melebihi manfaatnya.Apa yang seharusnya Anda lakukan, adalah menulis javadoc yang tepat untuk metode default, menunjukkan dengan tepat apa metode tersebut dan tidak diizinkan untuk melakukannya. Dengan cara itu, kelas yang mengimplementasikan antarmuka "tidak diizinkan" untuk mengubah implementasi, meskipun tidak ada jaminan.
Siapa pun dapat menulis
Collection
yang mematuhi antarmuka dan kemudian melakukan hal-hal dalam metode yang benar-benar berlawanan dengan intuisi, tidak ada cara untuk melindungi diri dari itu, selain menulis unit test yang luas.sumber
final
diputuskan untuk tidak diizinkan menggunakaninterface
metode Java 8 . Rasio biaya / manfaat yang tidak memadai adalah kandidat yang baik, tetapi sejauh ini, itu spekulasi.Kami menambahkan
default
kata kunci ke metode kami di dalaminterface
saat kami tahu bahwa kelas yang memperpanjanginterface
mungkin atau tidaknyaoverride
implementasi kami. Tetapi bagaimana jika kita ingin menambahkan metode yang kita tidak ingin kelas implementasinya menimpa? Ya, dua opsi tersedia untuk kita:default
final
metode.static
metode.Sekarang, Java mengatakan bahwa jika kita memiliki
class
dua atau lebih implementasiinterfaces
sehingga mereka memilikidefault
metode dengan nama dan tanda tangan metode yang sama persis yaitu duplikat, maka kita perlu menyediakan implementasi metode itu di kelas kita. Sekarang dalam haldefault
final
metode, kami tidak dapat memberikan implementasi dan kami macet. Dan itulah sebabnyafinal
kata kunci tidak digunakan dalam antarmuka.sumber