Mengapa "final" tidak diizinkan dalam metode antarmuka Java 8?

335

Salah satu fitur Java 8 yang paling berguna adalah defaultmetode baru pada antarmuka. Pada dasarnya ada dua alasan (mungkin ada yang lain) mengapa mereka diperkenalkan:

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 Senderkelas:

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, defaultdan finaljelas-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 staticdan finalpada 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 staticmetode 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?

Lukas Eder
sumber
10
Saya minta maaf karena menjadi basah kuyup, tetapi satu-satunya cara pertanyaan yang dinyatakan dalam judul akan dijawab dalam persyaratan SO adalah melalui kutipan dari Brian Goetz atau kelompok Ahli JSR. Saya mengerti bahwa BG telah meminta diskusi publik, tapi itulah yang bertentangan dengan persyaratan SO, karena 'terutama berdasarkan pendapat'. Tampak bagi saya bahwa tanggung jawab sedang ditundukkan di sini. Adalah tugas Kelompok Ahli, dan Proses Komunitas Jawa yang lebih luas, untuk merangsang diskusi dan menghasilkan pemikiran. Bukan SO. Jadi saya memilih untuk menutup sebagai 'berdasarkan pendapat'.
Marquis of Lorne
2
Seperti yang kita semua tahu, finalmencegah 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 menarik
Vince Emigh
22
@ EJP: "Jika saya bisa melakukan itu, Anda juga bisa, dan jawab pertanyaan Anda sendiri, yang karenanya tidak perlu ditanyakan." Itu akan berlaku untuk hampir semua pertanyaan di forum ini, kan? Saya selalu dapat Google beberapa topik selama 5 jam dan kemudian belajar sesuatu yang orang lain harus belajar dengan cara yang sama sulitnya. Atau kami menunggu beberapa menit lagi agar seseorang memberikan jawaban yang lebih baik sehingga semua orang di masa depan (termasuk 12 upvote dan 8 bintang sejauh ini) dapat mengambil untung, karena SO sangat dirujuk di Google. Jadi iya. Pertanyaan ini bisa masuk ke dalam formulir tanya jawab SO.
Lukas Eder
5
@VinceEmigh " ... melihat bagaimana Anda HARUS mengganti metode yang diwarisi dari antarmuka ... " Itu tidak benar di Java 8. Java 8 memungkinkan Anda untuk mengimplementasikan metode dalam antarmuka, yang berarti Anda tidak perlu mengimplementasikannya dalam mengimplementasikan kelas. Di sini, finalakan digunakan dalam mencegah implementasi kelas dari mengesampingkan implementasi standar metode antarmuka.
awksp
28
@ EJP Anda tidak pernah tahu: Brian Goetz dapat membalas !
assylias

Jawaban:

419

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:

interface A { 
    default void foo() { ... }
}

interface B { 
}

class C implements A, B { 
}

Di sini, semuanya baik; Cmewarisi foo()dari A. Sekarang seandainya Bdiubah untuk memiliki foometode, dengan default:

interface B { 
    default void foo() { ... }
}

Sekarang, ketika kita pergi untuk mengkompilasi ulang C, kompiler akan memberi tahu kita bahwa ia tidak tahu perilaku apa yang diwarisi foo(), jadi Charus menimpanya (dan dapat memilih untuk mendelegasikan A.super.foo()jika ingin mempertahankan perilaku yang sama.) Tetapi bagaimana jika Btelah membuat default-nya final, dan Atidak di bawah kendali penulis C? Sekarang Csudah tak dapat diperbaiki lagi; itu tidak dapat dikompilasi tanpa mengesampingkan foo(), tetapi tidak bisa mengesampingkan foo()jika itu final B.

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,

Brian Goetz
sumber
88
Fantastis melihat Anda menjawab pertanyaan tentang fitur bahasa baru! Sangat membantu mendapatkan klarifikasi tentang maksud dan detail desain ketika mencari tahu bagaimana kita seharusnya menggunakan fitur-fitur baru. Apakah orang lain yang terlibat dengan desain berkontribusi pada SO - atau apakah Anda hanya melakukan ini sendiri? Saya akan mengikuti jawaban Anda di bawah tag java-8 - Saya ingin tahu apakah ada orang lain yang melakukan hal yang sama sehingga saya dapat mengikuti mereka juga.
Shorn
10
@Shorn Stuart Marks telah aktif di tag java-8. Jeremy Manson telah diposting di masa lalu. Saya juga ingat melihat pesan dari Joshua Bloch tetapi tidak dapat menemukannya sekarang.
assylias
1
Selamat untuk datang dengan metode antarmuka standar sebagai cara yang jauh lebih elegan untuk mencapai apa yang C # lakukan dengan metode ekstensi yang kurang dipahami dan agak jelek diimplementasikan. Adapun pertanyaan ini, jawaban tentang ketidakmungkinan menyelesaikan konflik nama jenis menyelesaikan masalah, tetapi sisa alasan yang diberikan tidak meyakinkan filologi. (Jika saya ingin metode antarmuka menjadi final, maka Anda harus menganggap bahwa saya harus memiliki alasan sendiri yang cukup bagus untuk tidak mengizinkan siapa pun menyediakan implementasi yang berbeda dari milik saya.)
Mike Nakis
1
Namun, resolusi konflik nama dapat dicapai dengan cara yang mirip dengan bagaimana hal itu dilakukan dalam C #, dengan menambahkan nama antarmuka tertentu yang sedang diterapkan pada deklarasi metode implementasi. Jadi, yang saya bawa pulang dari semua ini adalah bahwa metode antarmuka default tidak bisa final di Jawa karena itu akan memerlukan modifikasi tambahan untuk sintaks, yang Anda merasa tidak enak. (Terlalu c-tajam mungkin?)
Mike Nakis
18
@Mencoba kelas Abstrak masih satu-satunya cara untuk memperkenalkan negara, atau menerapkan metode Objek inti. Metode default adalah untuk perilaku murni ; kelas abstrak adalah untuk perilaku ditambah dengan negara.
Brian Goetz
43

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:

Keputusan untuk menjadikan semua anggota antarmuka publik memang keputusan yang tidak menguntungkan. Bahwa setiap penggunaan antarmuka dalam desain internal memperlihatkan implementasi detail pribadi adalah hal yang besar.

Sangat sulit untuk memperbaikinya tanpa menambahkan nuansa yang tidak jelas atau kompatibilitas untuk bahasa tersebut. Pemutusan kompatibilitas yang besarnya dan potensi kehalusan akan terlihat tidak masuk akal sehingga solusi harus ada yang tidak merusak kode yang ada.

Dapat memperkenalkan kembali kata kunci 'paket' sebagai penentu akses yang layak. Tidak adanya specifier di antarmuka akan menyiratkan akses publik dan tidak adanya specifier di kelas menyiratkan akses-paket. Penentu mana yang masuk akal dalam antarmuka tidak jelas - terutama jika, untuk meminimalkan beban pengetahuan pada pengembang, kami harus memastikan bahwa akses-penentu memiliki arti yang sama di kedua kelas dan antarmuka jika mereka ada.

Dengan tidak adanya metode default, saya akan berspekulasi bahwa specifier anggota dalam sebuah antarmuka harus setidaknya terlihat sebagai antarmuka itu sendiri (sehingga antarmuka sebenarnya dapat diimplementasikan dalam semua konteks yang terlihat) - dengan metode default yang tidak sangat yakin.

Apakah sudah ada komunikasi yang jelas mengenai apakah ini bahkan mungkin diskusi dalam ruang lingkup? Jika tidak, sebaiknya diadakan di tempat lain.

Akhirnya jawaban Brian Goetz adalah:

Ya, ini sudah dieksplorasi.

Namun, izinkan saya menetapkan beberapa harapan realistis - fitur bahasa / VM memiliki waktu yang lama, bahkan yang tampak sepele seperti ini. Waktu untuk mengajukan ide fitur bahasa baru untuk Java SE 8 sudah cukup lama berlalu.

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 :

Dan Anda mendapatkan apa yang Anda inginkan. Itulah tepatnya yang ditambahkan fitur ini - pewarisan berganda perilaku. Tentu saja kami mengerti bahwa orang akan menggunakannya sebagai ciri. Dan kami telah bekerja keras untuk memastikan bahwa model pewarisan yang mereka tawarkan sederhana dan cukup bersih sehingga orang dapat memperoleh hasil yang baik dengan melakukannya dalam berbagai situasi yang luas. Kami telah, pada saat yang sama, memilih untuk tidak mendorong mereka melampaui batas dari apa yang bekerja dengan sederhana dan bersih, dan itu mengarah pada reaksi "aw, Anda tidak bertindak cukup jauh" dalam beberapa kasus. Tapi sungguh, sebagian besar utas ini tampaknya menggerutu bahwa gelas itu hanya 98% penuh. Saya akan mengambil 98% itu dan melanjutkannya!

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.

Edwin Dalorzo
sumber
4
Saya melihat bahwa saya seharusnya memasukkan nama lama, "metode bertahan", dalam pengembaraan googling saya pagi ini. +1 untuk menggali ini.
Marco13
1
Penggalian fakta bersejarah yang bagus. Kesimpulan Anda selaras dengan jawaban resmi
Lukas Eder
Saya tidak melihat mengapa itu akan merusak kompatibilitas. final tidak diizinkan sebelumnya. mereka sekarang memungkinkan untuk pribadi, tetapi tidak dilindungi. myeh ... private tidak dapat mengimplementasikan ... sebuah antarmuka memperluas antarmuka lain mungkin mengimplementasikan bagian-bagian dari antarmuka induk, namun itu harus mengekspos kemampuan overloading kepada orang lain ...
mmm
17

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 :

"Saya kira jika metode" final default "diizinkan, mereka mungkin perlu menulis ulang dari internal invecpecial ke invokeinterface yang terlihat oleh pengguna."

dan fakta-fakta sepele seperti itu suatu metode sama sekali tidak dianggap sebagai metode final (benar-benar) ketika itu adalah defaultmetode, 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 invokeinterfaceinstruksi dan dan metode panggilan kelas, sesuai dengan invokevirtualinstruksi: Untuk invokevirtualinstruksi, mungkin ada pencarian vtable sederhana , karena metode tersebut harus diwariskan dari superclass, atau diimplementasikan oleh kelas secara langsung. Berbeda dengan itu, invokeinterfacepanggilan 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,finalmetode 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.

Marco13
sumber
Terima kasih atas wawasan yang menarik. Karya John Rose adalah jejak yang menarik. Saya masih tidak setuju dengan @EJP. Sebagai contoh tandingan, lihat jawaban saya untuk pertanyaan yang sangat menarik, sangat mirip gaya oleh Peter Lawrey . Hal ini dimungkinkan untuk menggali fakta-fakta sejarah, dan saya selalu senang untuk menemukan mereka di sini di Stack Overflow (di mana lagi?). Tentu saja, jawaban Anda masih spekulatif, dan saya tidak 100% yakin bahwa rincian implementasi JVM akan menjadi alasan terakhir (pun intended) agar JLS ditulis dengan satu atau lain cara ...
Lukas Eder
@LukasEder Tentu, pertanyaan - pertanyaan semacam ini menarik dan IMHO cocok dengan pola Tanya Jawab. Saya pikir ada dua poin penting yang menyebabkan perselisihan di sini: Yang pertama adalah Anda meminta "alasan". Ini mungkin dalam banyak kasus tidak didokumentasikan secara resmi . Misalnya tidak ada "alasan" yang disebutkan dalam JLS mengapa tidak ada tanda unsigned int, lihat stackoverflow.com/questions/430346 ... ...
Marco13
... ... Yang kedua adalah Anda hanya meminta "kutipan otoritatif", yang mengurangi jumlah orang yang berani menulis jawaban dari "beberapa lusin" menjadi ... "kira-kira nol". Terlepas dari itu, saya tidak yakin tentang bagaimana pengembangan JVM dan penulisan JLS saling terkait, yaitu sejauh mana perkembangan mempengaruhi apa yang ditulis ke dalam JLS, tapi ... Saya akan menghindari spekulasi di sini; -)
Marco13
1
Saya masih mengistirahatkan koper saya. Lihat siapa yang menjawab saya pertanyaan lain :-) Sekarang akan selamanya menjadi jelas dengan jawaban otoritatif, di sini di Stack Overflow mengapa diputuskan untuk tidak mendukung synchronizedpada defaultmetode.
Lukas Eder
3
@LukasEder begitu, sama di sini. Siapa yang bisa mengharapkan itu? Alasannya agak meyakinkan, terutama untuk finalpertanyaan 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.
Marco13
4

Saya tidak akan berpikir itu perlu untuk menentukan finalpada 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 Collectionyang 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.

skiwi
sumber
2
Kontrak Javadoc adalah solusi yang valid untuk contoh konkret yang saya cantumkan dalam pertanyaan saya, tetapi pertanyaannya sebenarnya bukan tentang kenyamanan penggunaan metode antarmuka kasus. Pertanyaannya adalah tentang alasan otoritatif mengapa finaldiputuskan untuk tidak diizinkan menggunakan interfacemetode Java 8 . Rasio biaya / manfaat yang tidak memadai adalah kandidat yang baik, tetapi sejauh ini, itu spekulasi.
Lukas Eder
0

Kami menambahkan defaultkata kunci ke metode kami di dalam interfacesaat kami tahu bahwa kelas yang memperpanjang interfacemungkin atau tidaknya overrideimplementasi kami. Tetapi bagaimana jika kita ingin menambahkan metode yang kita tidak ingin kelas implementasinya menimpa? Ya, dua opsi tersedia untuk kita:

  1. Tambahkan default finalmetode.
  2. Tambahkan staticmetode.

Sekarang, Java mengatakan bahwa jika kita memiliki class dua atau lebih implementasi interfacessehingga mereka memiliki defaultmetode dengan nama dan tanda tangan metode yang sama persis yaitu duplikat, maka kita perlu menyediakan implementasi metode itu di kelas kita. Sekarang dalam hal default finalmetode, kami tidak dapat memberikan implementasi dan kami macet. Dan itulah sebabnya finalkata kunci tidak digunakan dalam antarmuka.

Ashutosh
sumber