Java 8 memungkinkan metode antarmuka statis
Dengan Java 8, antarmuka dapat memiliki metode statis. Mereka juga dapat memiliki metode contoh konkret, tetapi tidak bidang contoh.
Sebenarnya ada dua pertanyaan di sini:
- Mengapa, di masa lalu yang buruk, antarmuka tidak dapat berisi metode statis?
- Mengapa metode statis tidak dapat ditimpa?
Metode statis dalam antarmuka
Tidak ada alasan teknis yang kuat mengapa antarmuka tidak bisa memiliki metode statis di versi sebelumnya. Ini disimpulkan dengan baik oleh poster pertanyaan rangkap. Metode antarmuka statis awalnya dianggap sebagai perubahan bahasa kecil, dan kemudian ada proposal resmi untuk menambahkannya di Java 7, tetapi kemudian dibatalkan karena komplikasi yang tidak terduga.
Akhirnya, Java 8 memperkenalkan metode antarmuka statis, serta metode instan yang dapat dikesampingkan dengan implementasi default. Mereka masih tidak dapat memiliki bidang contoh. Fitur-fitur ini adalah bagian dari dukungan ekspresi lambda, dan Anda dapat membaca lebih banyak tentang mereka di Bagian H JSR 335.
Mengganti metode statis
Jawaban untuk pertanyaan kedua sedikit lebih rumit.
Metode statis dapat diselesaikan pada waktu kompilasi. Pengiriman dinamis masuk akal untuk metode contoh, di mana kompiler tidak dapat menentukan jenis objek konkret, dan, dengan demikian, tidak dapat menyelesaikan metode untuk memanggil. Tetapi menggunakan metode statis memerlukan kelas, dan karena kelas itu dikenal secara statis — pada waktu kompilasi — pengiriman dinamis tidak diperlukan.
Sedikit latar belakang tentang cara kerja metode contoh diperlukan untuk memahami apa yang terjadi di sini. Saya yakin implementasi sebenarnya sangat berbeda, tetapi izinkan saya menjelaskan gagasan saya tentang metode pengiriman, yang modelnya mengamati perilaku secara akurat.
Berpura-pura bahwa setiap kelas memiliki tabel hash yang memetakan tanda tangan metode (nama dan tipe parameter) ke potongan kode yang sebenarnya untuk mengimplementasikan metode. Ketika mesin virtual mencoba untuk memanggil metode pada contoh, itu meminta objek untuk kelasnya dan mencari tanda tangan yang diminta di tabel kelas. Jika badan metode ditemukan, itu dipanggil. Jika tidak, kelas induk dari kelas tersebut diperoleh, dan pencarian diulangi di sana. Ini berlanjut sampai metode ditemukan, atau tidak ada lagi kelas induk — yang menghasilkan a NoSuchMethodError
.
Jika superclass dan subclass keduanya memiliki entri di tabel mereka untuk tanda tangan metode yang sama, versi sub-kelas ditemui terlebih dahulu, dan versi superclass tidak pernah digunakan — ini adalah "override".
Sekarang, misalkan kita melewatkan instance objek dan mulai dengan subkelas. Resolusi dapat dilanjutkan seperti di atas, memberi Anda semacam metode statis "yang dapat ditimpa". Resolusi semua dapat terjadi pada waktu kompilasi, karena kompiler mulai dari kelas yang dikenal, daripada menunggu sampai runtime untuk meminta objek dari tipe yang tidak ditentukan untuk kelasnya. Tidak ada gunanya "mengganti" metode statis karena kita selalu dapat menentukan kelas yang berisi versi yang diinginkan.
Konstruktor "antarmuka"
Ini sedikit lebih banyak bahan untuk membahas suntingan terbaru untuk pertanyaan itu.
Sepertinya Anda ingin secara efektif mengamanatkan metode seperti konstruktor untuk setiap implementasi IXMLizable
. Lupakan tentang mencoba menegakkan ini dengan antarmuka sebentar, dan berpura-pura bahwa Anda memiliki beberapa kelas yang memenuhi persyaratan ini. Bagaimana Anda menggunakannya?
class Foo implements IXMLizable<Foo> {
public static Foo newInstanceFromXML(Element e) { ... }
}
Foo obj = Foo.newInstanceFromXML(e);
Karena Anda harus secara eksplisit memberi nama tipe konkret Foo
ketika "membangun" objek baru, kompilator dapat memverifikasi bahwa ia memang memiliki metode pabrik yang diperlukan. Dan jika tidak, lalu bagaimana? Jika saya bisa menerapkan IXMLizable
yang tidak memiliki "konstruktor", dan saya membuat sebuah instance dan menyebarkannya ke kode Anda, itu adalah sebuah IXMLizable
dengan semua antarmuka yang diperlukan.
Konstruksi adalah bagian dari implementasi, bukan antarmuka. Kode apa pun yang berhasil bekerja dengan antarmuka tidak peduli tentang konstruktor. Kode apa pun yang peduli tentang konstruktor perlu mengetahui tipe konkretnya, dan antarmuka dapat diabaikan.