Saya menjawab pertanyaan dan mengalami skenario yang tidak bisa saya jelaskan. Pertimbangkan kode ini:
interface ConsumerOne<T> {
void accept(T a);
}
interface CustomIterable<T> extends Iterable<T> {
void forEach(ConsumerOne<? super T> c); //overload
}
class A {
private static CustomIterable<A> iterable;
private static List<A> aList;
public static void main(String[] args) {
iterable.forEach(a -> aList.add(a)); //ambiguous
iterable.forEach(aList::add); //ambiguous
iterable.forEach((A a) -> aList.add(a)); //OK
}
}
Saya tidak mengerti mengapa secara eksplisit mengetik paramter dari lambda (A a) -> aList.add(a)
membuat kode dikompilasi. Selain itu, mengapa tautan ke kelebihan di dalam Iterable
daripada di CustomIterable
?
Apakah ada penjelasan untuk ini atau tautan ke bagian spesifikasi yang relevan?
Catatan: iterable.forEach((A a) -> aList.add(a));
hanya mengkompilasi ketika CustomIterable<T>
diperluas Iterable<T>
(secara berlebihan membebani metode dalam CustomIterable
hasil dalam kesalahan ambigu)
Mendapatkan ini pada keduanya:
- versi openjdk "13.0.2" 2020-01-14
Compiler Eclipse - versi openjdk "1.8.0_232"
Kompilator Eclipse
Sunting : Kode di atas gagal dikompilasi pada bangunan dengan pakar sementara Eclipse berhasil mengkompilasi baris kode terakhir.
Jawaban:
TL; DR, ini adalah bug penyusun.
Tidak ada aturan yang akan diutamakan untuk metode tertentu yang berlaku ketika diwariskan atau metode default. Menariknya, ketika saya mengubah kode ke
yang
iterable.forEach((A a) -> aList.add(a));
pernyataan menghasilkan kesalahan dalam Eclipse.Karena tidak ada properti
forEach(Consumer<? super T) c)
metode dariIterable<T>
antarmuka berubah ketika menyatakan kelebihan lainnya, keputusan Eclipse untuk memilih metode ini tidak dapat (secara konsisten) didasarkan pada properti metode apa pun. Itu masih satu-satunya metode yang diwariskan, masih satu-satunyadefault
metode, masih satu-satunya metode JDK, dan seterusnya. Tak satu pun dari properti ini yang akan memengaruhi pemilihan metode.Perhatikan bahwa mengubah deklarasi menjadi
juga menghasilkan kesalahan "ambigu", sehingga jumlah metode kelebihan beban yang berlaku juga tidak masalah, bahkan ketika hanya ada dua kandidat, tidak ada preferensi umum terhadap
default
metode.Sejauh ini, masalah tampaknya muncul ketika ada dua metode yang berlaku dan
default
metode dan hubungan warisan yang terlibat, tetapi ini bukan tempat yang tepat untuk menggali lebih jauh.Tetapi dapat dimengerti bahwa konstruk dari contoh Anda dapat ditangani oleh kode implementasi yang berbeda di kompiler, yang satu menunjukkan bug sementara yang lain tidak.
a -> aList.add(a)
adalah ekspresi lambda yang diketik secara implisit , yang tidak dapat digunakan untuk resolusi kelebihan beban. Sebaliknya,(A a) -> aList.add(a)
adalah ekspresi lambda yang diketik secara eksplisit yang dapat digunakan untuk memilih metode yang cocok dari metode kelebihan beban, tetapi itu tidak membantu di sini (seharusnya tidak membantu di sini), karena semua metode memiliki tipe parameter dengan tanda tangan fungsional yang persis sama .Sebagai contoh tandingan, dengan
tanda tangan fungsional berbeda, dan menggunakan ekspresi lambda tipe eksplisit memang dapat membantu memilih metode yang tepat sedangkan ekspresi lambda yang diketik secara implisit tidak membantu, sehingga
forEach(s -> s.isEmpty())
menghasilkan kesalahan kompilator. Dan semua kompiler Java setuju tentang itu.Perhatikan bahwa
aList::add
ini adalah referensi metode yang ambigu, karenaadd
metode ini kelebihan beban juga, jadi itu juga tidak dapat membantu memilih metode, tetapi referensi metode mungkin akan diproses oleh kode yang berbeda pula. Beralih ke yang ambiguaList::contains
atau berubahList
menjadiCollection
, untuk membuatadd
tidak ambigu, tidak mengubah hasil di instalasi Eclipse saya (saya menggunakan2019-06
).sumber
default
metode hanyalah poin tambahan. Jawaban saya sudah menunjukkan contoh di mana Eclipse tidak memberikan prioritas pada metode default.default
mengubah hasil dan segera berasumsi untuk menemukan alasan perilaku yang diamati. Anda terlalu percaya diri tentang hal itu sehingga Anda menyebut jawaban lain salah, terlepas dari kenyataan bahwa mereka bahkan tidak bertentangan. Karena Anda memproyeksikan perilaku Anda sendiri terhadap orang lain, saya tidak pernah mengklaim bahwa warisan adalah alasannya . Saya membuktikan bahwa itu tidak benar. Saya menunjukkan bahwa perilaku itu tidak konsisten, karena Eclipse memilih metode tertentu dalam satu skenario tetapi tidak di yang lain, di mana ada tiga kelebihan.default
yang lainabstract
, dengan dua argumen seperti konsumen dan mencobanya. Eclipse dengan benar mengatakan bahwa itu ambigu, meskipun ada satudefault
metode. Tampaknya warisan masih relevan untuk bug Eclipse ini, tetapi tidak seperti Anda, saya tidak akan marah dan menyebut jawaban lain salah, hanya karena mereka tidak menganalisis bug secara keseluruhan. Itu bukan tugas kita di sini.Kompilator Eclipse dengan benar menyelesaikan ke
default
metode , karena ini adalah metode yang paling spesifik sesuai dengan Spesifikasi Bahasa Jawa 15.12.2.5 :javac
(digunakan oleh Maven dan IntelliJ secara default) memberi tahu pemanggilan metode bersifat ambigu di sini. Tetapi menurut Spesifikasi Bahasa Jawa itu tidak ambigu karena salah satu dari dua metode adalah metode yang paling spesifik di sini.Ekspresi lambda yang diketik secara implisit ditangani secara berbeda dari ekspresi lambda yang diketik secara eksplisit di Jawa. Secara implisit diketik berbeda dengan ekspresi lambda yang diketik secara eksplisit, jatuh pada fase pertama untuk mengidentifikasi metode doa yang ketat (lihat Spesifikasi Bahasa Jawa jls-15.12.2.2 , poin pertama). Oleh karena itu, pemanggilan metode di sini bersifat ambigu untuk ekspresi lambda yang diketik secara implisit .
Dalam kasus Anda, solusi untuk
javac
bug ini adalah menentukan jenis antarmuka fungsional alih-alih menggunakan ekspresi lambda yang diketik secara eksplisit sebagai berikut:atau
Berikut adalah contoh Anda yang diperkecil lebih lanjut untuk pengujian:
sumber
default
metode ketika ada tiga metode kandidat atau ketika kedua metode dinyatakan dalam antarmuka yang sama.forEach(Consumer)
danforEach(Consumer2)
yang tidak pernah berakhir pada metode implementasi yang sama.Kode di mana Eclipse mengimplementasikan JLS §15.12.2.5 tidak menemukan metode mana pun lebih spesifik daripada yang lain, bahkan untuk kasus lambda yang diketik secara eksplisit.
Jadi idealnya Eclipse akan berhenti di sini dan melaporkan ambiguitas. Sayangnya, implementasi resolusi kelebihan memiliki kode non-sepele selain menerapkan JLS. Dari pemahaman saya kode ini (yang berasal dari saat Java 5 baru) harus disimpan untuk mengisi beberapa celah di JLS.
Saya telah mengajukan https://bugs.eclipse.org/562538 untuk melacak ini.
Terlepas dari bug khusus ini, saya hanya bisa sangat menyarankan terhadap gaya kode ini. Overloading baik untuk sejumlah kejutan di Jawa, dikalikan dengan inferensi tipe lambda, kompleksitas cukup di luar proporsi sesuai dengan perolehan yang dirasakan.
sumber