Ini adalah satu lagi masalah desain bahasa yang tampaknya "jelas merupakan ide yang bagus" sampai Anda mulai menggali dan Anda menyadari bahwa itu sebenarnya ide yang buruk.
Email ini memiliki banyak hal tentang subjek (dan juga subjek lainnya.) Ada beberapa kekuatan desain yang berkumpul untuk membawa kami ke desain saat ini:
- Keinginan untuk menjaga model warisan tetap sederhana;
- Fakta bahwa setelah Anda melihat contoh-contoh yang jelas (misalnya, berubah
AbstractList
menjadi antarmuka), Anda menyadari bahwa mewarisi sama dengan / kode hash / toString sangat terkait dengan warisan tunggal dan negara, dan antarmuka multipel warisan dan stateless;
- Bahwa itu berpotensi membuka pintu ke beberapa perilaku mengejutkan.
Anda telah menyentuh tujuan "tetap sederhana"; aturan pewarisan dan resolusi konflik dirancang untuk menjadi sangat sederhana (kelas menang atas antarmuka, antarmuka turunan menang atas superinterfaces, dan konflik lainnya diselesaikan oleh kelas pelaksana.) Tentu saja aturan ini dapat diubah untuk membuat pengecualian, tetapi Saya pikir Anda akan menemukan ketika Anda mulai menarik tali itu, bahwa kompleksitas tambahannya tidak sekecil yang Anda kira.
Tentu saja, ada beberapa tingkat manfaat yang akan membenarkan lebih banyak kerumitan, tetapi dalam kasus ini tidak ada. Metode yang kita bicarakan di sini adalah sama, kode hash, dan toString. Semua metode ini secara intrinsik tentang status objek, dan kelaslah yang memiliki status, bukan antarmuka, yang berada dalam posisi terbaik untuk menentukan apa arti kesetaraan untuk kelas itu (terutama karena kontrak untuk kesetaraan cukup kuat; lihat Efektif Java untuk beberapa konsekuensi yang mengejutkan); penulis antarmuka terlalu jauh dihapus.
Sangat mudah untuk menarik AbstractList
contohnya; alangkah baiknya jika kita bisa menyingkirkan AbstractList
dan menempatkan perilaku ke List
antarmuka. Tetapi begitu Anda melangkah melampaui contoh nyata ini, tidak ada banyak contoh bagus lainnya yang dapat ditemukan. Pada dasarnya, AbstractList
dirancang untuk pewarisan tunggal. Tetapi interface harus dirancang untuk multiple inheritance.
Selanjutnya, bayangkan Anda menulis kelas ini:
class Foo implements com.libraryA.Bar, com.libraryB.Moo {
// Implementation of Foo, that does NOT override equals
}
The Foo
penulis terlihat di supertypes, tidak melihat pelaksanaan equals, dan menyimpulkan bahwa untuk mendapatkan referensi kesetaraan, semua yang dia perlu lakukan adalah sama dengan mewarisi dari Object
. Kemudian, minggu depan, pengelola perpustakaan untuk Bar "membantu" menambahkan equals
implementasi default . Ups! Sekarang semantik Foo
telah dipecah oleh sebuah antarmuka di domain pemeliharaan lain "sangat membantu" menambahkan default untuk metode umum.
Default seharusnya adalah default. Menambahkan default ke antarmuka di mana tidak ada (di mana pun dalam hierarki) seharusnya tidak memengaruhi semantik kelas implementasi konkret. Tetapi jika default bisa "menimpa" metode objek, itu tidak benar.
Jadi, walaupun tampak seperti fitur yang tidak berbahaya, sebenarnya cukup berbahaya: ia menambahkan banyak kerumitan untuk sedikit ekspresif tambahan, dan itu membuatnya terlalu mudah untuk perubahan yang bermaksud baik, tampak tidak berbahaya ke antarmuka yang dikompilasi secara terpisah untuk melemahkan semantik yang dimaksudkan dari kelas implementasi.