Comparator.reversed () tidak dikompilasi menggunakan lambda

111

Saya memiliki daftar dengan beberapa objek Pengguna dan saya mencoba mengurutkan daftar, tetapi hanya berfungsi menggunakan referensi metode, dengan ekspresi lambda, kompilator memberikan kesalahan:

List<User> userList = Arrays.asList(u1, u2, u3);
userList.sort(Comparator.comparing(u -> u.getName())); // works
userList.sort(Comparator.comparing(User::getName).reversed()); // works
userList.sort(Comparator.comparing(u -> u.getName()).reversed()); // Compiler error

Kesalahan:

com\java8\collectionapi\CollectionTest.java:35: error: cannot find symbol
            userList.sort(Comparator.comparing(u -> u.getName()).reversed());
                                                     ^
symbol:   method getName()
location: variable u of type Object
1 error
Andrey
sumber

Jawaban:

145

Ini adalah kelemahan dalam mekanisme inferensial tipe compiler. Untuk menyimpulkan jenis ulambda, jenis target untuk lambda perlu ditetapkan. Ini dicapai sebagai berikut. userList.sort()mengharapkan argumen tipe Comparator<User>. Di baris pertama, Comparator.comparing()perlu dikembalikan Comparator<User>. Ini menyiratkan bahwa Comparator.comparing()kebutuhan Functionyang membutuhkan Userargumen. Jadi di lambda di baris pertama, uharus dari jenis Userdan semuanya berfungsi.

Di baris kedua dan ketiga, pengetikan target terganggu oleh adanya panggilan ke reversed(). Saya tidak sepenuhnya yakin mengapa; baik penerima dan jenis kembalinya reversed()yang Comparator<T>jadi sepertinya jenis target harus disebarkan kembali ke penerima, tetapi tidak. (Seperti yang saya katakan, itu kelemahan.)

Di baris kedua, referensi metode memberikan informasi jenis tambahan yang mengisi celah ini. Informasi ini tidak ada di baris ketiga, jadi kompilator menyimpulkan umenjadi Object(fallback inferensi dari upaya terakhir), yang gagal.

Jelas jika Anda dapat menggunakan referensi metode, lakukan itu dan itu akan berhasil. Terkadang Anda tidak dapat menggunakan referensi metode, misalnya, jika Anda ingin meneruskan parameter tambahan, jadi Anda harus menggunakan ekspresi lambda. Dalam hal ini Anda akan memberikan tipe parameter eksplisit di lambda:

userList.sort(Comparator.comparing((User u) -> u.getName()).reversed());

Mungkin kompilator dapat ditingkatkan untuk mencakup kasus ini di rilis mendatang.

Stuart Marks
sumber
28
Lambdas dibagi menjadi tipe implisit (tidak ada tipe manifes untuk parameter) dan tipe eksplisit ; referensi metode dibagi menjadi tepat (tidak ada kelebihan beban) dan tidak tepat . Ketika panggilan metode umum dalam posisi penerima memiliki argumen lambda, dan parameter tipe tidak dapat sepenuhnya disimpulkan dari argumen lain, Anda perlu menyediakan lambda eksplisit, metode ref tepat, tipe target cast, atau saksi tipe eksplisit untuk panggilan metode umum untuk memberikan informasi jenis tambahan yang diperlukan untuk melanjutkan.
Brian Goetz
1
@StuartMarks, Anda "tidak sepenuhnya yakin mengapa" kompilator bertindak seperti ini. Tapi apa spesifikasi bahasanya ? Haruskah tersedia informasi yang cukup untuk menentukan tipe generik, sesuai dengan spesifikasi bahasa? Jika demikian, ini adalah bug kompilator dan harus diajukan dan ditangani sebagaimana mestinya. Jika tidak, itu adalah area di mana bahasa Jawa harus ditingkatkan. Yang mana
Garret Wilson
8
Saya pikir kami dapat menganggap komentar Brian sebagai definitif, karena dia menulis spesifikasi yang dipermasalahkan :-)
minimalis
1
Sayangnya tidak ada dari ini yang menjelaskan mengapa itu berfungsi tanpa dibalik sementara itu tidak berfungsi dengan terbalik.
Chris311
90

Anda bisa mengatasi batasan ini dengan menggunakan argumen dua Comparator.comparingwith Comparator.reverseOrder()sebagai argumen kedua:

users.sort(comparing(User::getName, reverseOrder()));
Misha
sumber
4
Bagus. Saya lebih suka ini daripada menggunakan lambda yang diketik secara eksplisit. Atau, lebih baik lagi users.sort(reverseOrder(comparing(User::getName)));.
putar
10
Perhatikan bahwa reverseOrder(Comparator<T>)metode di atas adalah dalam java.util.Collections, bukan dalam Comparator.
rolve