Comparator.reversed () non viene compilato utilizzando lambda


111

Ho un elenco con alcuni oggetti utente e sto cercando di ordinare l'elenco, ma funziona solo usando il riferimento al metodo, con l'espressione lambda il compilatore dà un errore:

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

Errore:

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

Risposte:


145

Questa è una debolezza nel meccanismo di inferenza del tipo del compilatore. Per dedurre il tipo di ulambda, è necessario stabilire il tipo di destinazione per lambda. Ciò si ottiene come segue. userList.sort()si aspetta un argomento di tipo Comparator<User>. Nella prima riga, Comparator.comparing()deve tornare Comparator<User>. Ciò implica che Comparator.comparing()necessita di una Functionche accetta un Userargomento. Quindi nel lambda sulla prima riga, udeve essere di tipo Usere tutto funziona.

Nella seconda e terza riga, la digitazione del target è interrotta dalla presenza della chiamata a reversed(). Non sono del tutto sicuro del perché; sia il ricevitore che il tipo di ritorno reversed()sono Comparator<T>così sembra che il tipo di destinazione dovrebbe essere propagato al ricevitore, ma non lo è. (Come ho detto, è una debolezza.)

Nella seconda riga, il riferimento al metodo fornisce ulteriori informazioni sul tipo che colmano questa lacuna. Questa informazione è assente dalla terza riga, quindi il compilatore deduce uche sia Object(l'inferenza fallback di ultima istanza), che fallisce.

Ovviamente se puoi usare un metodo di riferimento, fallo e funzionerà. A volte non è possibile utilizzare un riferimento al metodo, ad esempio, se si desidera passare un parametro aggiuntivo, quindi è necessario utilizzare un'espressione lambda. In tal caso, forniresti un tipo di parametro esplicito nel lambda:

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

Potrebbe essere possibile migliorare il compilatore per coprire questo caso in una versione futura.


28
I Lambda sono divisi in tipizzati implicitamente (nessun tipo manifest per i parametri) e tipizzati in modo esplicito ; i riferimenti ai metodi sono divisi in esatti (senza sovraccarichi) e inesatti . Quando una chiamata a un metodo generico in una posizione del ricevitore ha argomenti lambda e i parametri di tipo non possono essere completamente dedotti dagli altri argomenti, è necessario fornire un lambda esplicito, un riferimento di metodo esatto, un cast di tipo target o testimoni di tipo espliciti per la chiamata al metodo generico per fornire le informazioni aggiuntive sul tipo necessarie per procedere.
Brian Goetz

1
@StuartMarks, "non sei del tutto sicuro del perché" il compilatore si comporti in questo modo. Ma cosa dice la specifica della lingua ? Dovrebbero esserci informazioni sufficienti per determinare i tipi generici, secondo la specifica del linguaggio? In tal caso, si tratta di un bug del compilatore e dovrebbe essere archiviato e gestito di conseguenza. Altrimenti è un'area in cui il linguaggio Java dovrebbe essere migliorato. Cos'è questo?
Garret Wilson

8
Penso che possiamo considerare i commenti di Brian come definitivi, poiché ha scritto la specifica in questione :-)
minimalis

1
Purtroppo niente di tutto questo spiega perché funziona senza invertito mentre non funziona con invertito.
Chris311

90

Puoi aggirare questa limitazione usando i due argomenti Comparator.comparingcon Comparator.reverseOrder()come secondo argomento:

users.sort(comparing(User::getName, reverseOrder()));

4
Bello. Mi piace di più rispetto all'utilizzo di un lambda tipizzato in modo esplicito. O, meglio ancora, users.sort(reverseOrder(comparing(User::getName)));.
ruolo

10
Nota che il reverseOrder(Comparator<T>)metodo sopra è in java.util.Collections, non in Comparator.
ruolo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.