Metode default adalah alat baru yang bagus di kotak alat Java kami. Namun, saya mencoba menulis antarmuka yang mendefinisikan default
versi toString
metode ini. Java memberi tahu saya bahwa ini dilarang, karena metode yang dideklarasikan di java.lang.Object
mungkin tidak default
diedit. Mengapa demikian?
Saya tahu bahwa ada aturan "kelas dasar selalu menang", jadi secara default (pun;), setiap default
implementasi Object
metode akan ditimpa oleh metode dari Object
pokoknya. Namun, saya tidak melihat alasan mengapa seharusnya tidak ada pengecualian untuk metode dari Object
dalam spesifikasi. Khusus untuk toString
itu mungkin sangat berguna untuk memiliki implementasi standar.
Jadi, apa alasan mengapa desainer Java memutuskan untuk tidak mengizinkan default
metode yang menimpa metode Object
?
sumber
Jawaban:
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:
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;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 menyingkirkanAbstractList
dan menempatkan perilaku keList
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:
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 dariObject
. Kemudian, minggu depan, pengelola perpustakaan untuk Bar "membantu" menambahkanequals
implementasi default . Ups! Sekarang semantikFoo
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.
sumber
hashCode
danequals
, tetapi saya pikir ini akan sangat bergunatoString
. Sebagai contoh, beberapaDisplayable
antarmuka mungkin mendefinisikan sebuahString display()
metode, dan itu akan menghemat satu ton boilerplate untuk dapat menentukandefault String toString() { return display(); }
diDisplayable
, bukan membutuhkan setiap satuDisplayable
untuk melaksanakantoString()
atau memperpanjangDisplayableToString
kelas dasar.toString()
hanya didasarkan pada metode antarmuka, Anda bisa menambahkan sesuatu sepertidefault String toStringImpl()
antarmuka, dan menimpatoString()
di setiap subkelas untuk memanggil implementasi antarmuka - sedikit jelek, tetapi berfungsi, dan lebih baik daripada tidak sama sekali. :) Cara lain untuk melakukannya adalah membuat sesuatu sepertiObjects.hash()
,Arrays.deepEquals()
danArrays.deepToString()
. Memberi +1 pada jawaban @ BrianGoetz!default toString()
override dalam antarmuka fungsional akan memungkinkan kita untuk - setidaknya - melakukan sesuatu seperti memuntahkan tanda tangan fungsi dan kelas induk dari implementer. Lebih baik lagi, jika kita bisa membawa beberapa strategi pengulangan ke String untuk ditanggung kita bisa berjalan melalui penutupan untuk mendapatkan deskripsi yang sangat baik tentang lambda, dan dengan demikian meningkatkan kurva belajar lambda secara drastis.Dilarang mendefinisikan metode default di antarmuka untuk metode dalam
java.lang.Object
, karena metode default tidak akan pernah "terjangkau".Metode antarmuka default dapat ditimpa dalam kelas yang mengimplementasikan antarmuka dan implementasi kelas metode memiliki prioritas lebih tinggi daripada implementasi antarmuka, bahkan jika metode ini diterapkan dalam superclass. Karena semua kelas mewarisi
java.lang.Object
, metode dijava.lang.Object
akan lebih diutamakan daripada metode default di antarmuka dan dipanggil sebagai gantinya.Brian Goetz dari Oracle memberikan beberapa rincian lebih lanjut tentang keputusan desain di pos milis ini .
sumber
Saya tidak melihat ke kepala penulis bahasa Jawa, jadi kami mungkin hanya menebak. Tetapi saya melihat banyak alasan dan sepenuhnya setuju dengan mereka dalam masalah ini.
Alasan utama untuk memperkenalkan metode default adalah untuk dapat menambahkan metode baru ke antarmuka tanpa melanggar kompatibilitas mundur dari implementasi yang lebih lama. Metode default juga dapat digunakan untuk menyediakan metode "kenyamanan" tanpa keharusan untuk mendefinisikannya di setiap kelas implementasi.
Tak satu pun dari ini berlaku untuk toString dan metode Object lainnya. Sederhananya, metode default dirancang untuk memberikan perilaku default di mana tidak ada definisi lain. Tidak menyediakan implementasi yang akan "bersaing" dengan implementasi lain yang ada.
Aturan "kelas dasar selalu menang" memiliki alasan kuat juga. Seharusnya kelas mendefinisikan implementasi nyata , sementara antarmuka mendefinisikan implementasi standar , yang agak lemah.
Juga, memperkenalkan pengecualian APAPUN pada aturan umum menyebabkan kompleksitas yang tidak perlu dan mengajukan pertanyaan lain. Obyek adalah (kurang lebih) kelas seperti yang lain, jadi mengapa harus memiliki perilaku yang berbeda?
Semua dan semua, solusi yang Anda usulkan mungkin akan membawa lebih banyak kontra daripada pro.
sumber
Alasannya sangat sederhana, itu karena Object adalah kelas dasar untuk semua kelas Java. Jadi, bahkan jika kita memiliki metode Object didefinisikan sebagai metode default di beberapa antarmuka, itu akan sia-sia karena metode Object akan selalu digunakan. Itulah sebabnya untuk menghindari kebingungan, kita tidak dapat memiliki metode default yang mengesampingkan metode kelas Objek.
sumber
Untuk memberikan jawaban yang sangat bagus, hanya dilarang mendefinisikan
default
metode untuk metode publikjava.lang.Object
. Ada 11 metode untuk dipertimbangkan, yang dapat dikategorikan dalam tiga cara untuk menjawab pertanyaan ini.Object
metode tidak dapat memilikidefault
metode karena merekafinal
dan tidak dapat ditimpa sama sekali:getClass()
,notify()
,notifyAll()
,wait()
,wait(long)
, danwait(long, int)
.Object
metode tidak dapat memilikidefault
metode untuk alasan yang diberikan di atas oleh Brian Goetz:equals(Object)
,hashCode()
, dantoString()
.Dua
Object
metode dapat memilikidefault
metode, meskipun nilai default seperti itu dipertanyakan di terbaik:clone()
danfinalize()
.sumber