Comparator.reversed()はラムダを使用してコンパイルしません


111

いくつかのUserオブジェクトを含むリストがあり、リストを並べ替えようとしていますが、メソッド参照を使用した場合にのみ機能します。ラムダ式を使用すると、コンパイラによってエラーが発生します。

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

エラー:

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

回答:


145

これは、コンパイラの型推論メカニズムの弱点です。uラムダのの型を推測するには、ラムダのターゲット型を確立する必要があります。これは次のように行われます。userList.sort()型の引数が必要ですComparator<User>。最初の行でComparator.comparing()は、を返す必要がありますComparator<User>。これは、引数を取るがComparator.comparing()必要であるFunctionことを意味しますUser。したがって、最初の行のラムダではu、タイプである必要がありUser、すべてが機能します。

2行目と3行目では、への呼び出しの存在によってターゲットのタイピングが中断されていますreversed()。理由はよくわかりません。レシーバと戻り値の型はどちらもそうなので、ターゲットの型をレシーバに伝播する必要reversed()があるComparator<T>ように見えますが、そうではありません。(私が言ったように、それは弱点です。)

2行目のメソッドリファレンスは、このギャップを埋める追加の型情報を提供します。コンパイラ推論がので、この情報は、第三の線に存在しないuことがObject失敗した(最後の推論フォールバック)。

もちろん、メソッド参照を使用できる場合は、それを実行すれば機能します。場合によっては、メソッド参照を使用できないことがあります。たとえば、追加のパラメーターを渡したい場合は、ラムダ式を使用する必要があります。その場合は、ラムダに明示的なパラメータータイプを指定します。

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

将来のリリースでこのケースをカバーするようにコンパイラーを拡張できる可能性があります。


28
ラムダは、暗黙的に型付けされた(パラメーターのマニフェスト型なし)と明示的に型付けされたに分けられます。メソッド参照は、exact(オーバーロードなし)とinexactに分けられます。レシーバー位置でのジェネリックメソッド呼び出しにラムダ引数があり、型パラメーターを他の引数から完全に推測できない場合は、明示的なラムダ、正確なメソッド参照、ターゲットの型キャスト、または明示的な型の証人のいずれかを提供する必要があります続行するために必要な追加の型情報を提供するためのジェネリックメソッド呼び出し。
Brian Goetz 2014

1
@StuartMarks、あなたはコンパイラーがこのように動作していることを「完全にはなぜ確信していない」のです。しかし、言語仕様は何と言っていますか?言語仕様に従って、ジェネリック型を決定するための十分な情報があるはずですか?もしそうなら、これはコンパイラのバグであり、提出し、それに応じて処理する必要があります。それ以外の場合は、Java言語を改善する必要がある領域です。どっち?
Garret Wilson、

8
ブライアンが問題の仕様を書いたので、ブライアンのコメントを決定的なものとして扱うことができると思います:-)
minimalis

1
悲しいことに、これは逆転なしでは機能するのに逆転なしでは機能する理由を説明していません。
Chris311、19年

90

2番目の引数として2つの引数Comparator.comparingを使用することにより、この制限を回避できComparator.reverseOrder()ます。

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

4
いいね。これは、明示的に型指定されたラムダを使用するよりも好きです。または、いっそのこと、users.sort(reverseOrder(comparing(User::getName)));
rolve 2015年

10
reverseOrder(Comparator<T>)上記のメソッドは内java.util.Collectionsではなく内にあることに注意してくださいComparator
rolve 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.