ラムダ式とジェネリックメソッド


111

私が一般的なインターフェースを持っているとしましょう:

interface MyComparable<T extends Comparable<T>>  {
    public int compare(T obj1, T obj2);
}

そして方法sort

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable<T> comp) {
    // sort the list
}

このメソッドを呼び出して、ラムダ式を引数として渡すことができます。

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

それはうまくいきます。

しかし、インターフェイスをジェネリックにせず、メソッドをジェネリックにすると、次のようになります。

interface MyComparable {
    public <T extends Comparable<T>> int compare(T obj1, T obj2);
}

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable comp) {
}

そして、これを次のように呼び出します:

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

コンパイルされません。それはラムダ式でのエラーを示しています:

「ターゲットメソッドはジェネリック」

OK、を使用してコンパイルするとjavac、次のエラーが表示されます。

SO.java:20: error: incompatible types: cannot infer type-variable(s) T#1
        sort(list, (a, b) -> a.compareTo(b));
            ^
    (argument mismatch; invalid functional descriptor for lambda expression
      method <T#2>(T#2,T#2)int in interface MyComparable is generic)
  where T#1,T#2 are type-variables:
    T#1 extends Comparable<T#1> declared in method <T#1>sort(List<T#1>,MyComparable)
    T#2 extends Comparable<T#2> declared in method <T#2>compare(T#2,T#2)
1 error

このエラーメッセージから、コンパイラーが型引数を推測できないようです。それは事実ですか?はいの場合、なぜこのようになっているのですか?

インターネットでいろいろな方法で検索してみました。次に、方法を示すこのJavaCodeGeeks記事を見つけたので、試してみました。

sort(list, <T extends Comparable<T>>(a, b) -> a.compareTo(b));

これは、その記事がそれが機能すると主張していることとは対照的に、再び機能しません。一部の初期ビルドで機能していた可能性があります。

だから私の質問です:ジェネリックメソッドのラムダ式を作成する方法はありますか?ただし、メソッドを作成することで、メソッド参照を使用してこれを行うことができます。

public static <T extends Comparable<T>> int compare(T obj1, T obj2) {
    return obj1.compareTo(obj2);
}

一部のクラスではと言いSO、次のように渡します。

sort(list, SO::compare);

回答:


117

関数インターフェイスのメソッドに型パラメーターがある場合、関数型インターフェイスにラムダ式を使用することはできません。JLS8のセクション§15.27.3を参照してください。

Tが関数型インターフェースタイプ(§9.8)であり、式が[..] Tの関数型と一致する場合、ラムダ式はターゲット型Tと互換性があります[..]。ラムダ式は一致します。次のすべてに該当する場合は、関数タイプを使用します。

  • 関数型には型パラメーターがありません
  • [..]

47
ただし、この制限はジェネリックメソッドへのメソッド参照には適用されません。ジェネリック関数インターフェースを持つジェネリックメソッドへのメソッド参照を使用できます。
ブライアンゲッツ2014年

17
この制限には十分な理由があると思います。それは何ですか?
Sandro

6
@Sandro:ラムダ式の型パラメーターを宣言する構文はありません。そして、そのような構文は非常に複雑になります。パーサーは、他の正当なJavaコンストラクトとは別に、型パラメーターを使用してそのようなラムダ式を通知できる必要があることに注意してください。したがって、メソッド参照を使用する必要があります。ターゲットメソッド、確立された構文を使用して型パラメーターを宣言できます。
Holger

2
@Holgerは依然として、型パラメーターを自動推定できる場合、たとえばSet <?>を宣言し、それらのキャプチャーされた型で型チェックを行う場合と同様に、コンパイラーはcapure型を決定できます。確かに、それを本文の型パラメータとして指定することは不可能ですが、必要な場合は、メソッド参照を使用することをお勧めします
WorldSEnder

17

メソッド参照を使用して、引数を渡す別の方法を見つけました:

List<String> list = Arrays.asList("a", "b", "c");        
sort(list, Comparable::<String>compareTo);

3

コンパイラに一般的なコンパレータの適切なバージョンを指定するだけです (Comparator<String>)

だから答えは

sort(list, (Comparator<String>)(a, b) -> a.compareTo(b));


2
incompatible types: java.util.Comparator<java.lang.String> cannot be converted to MyComparableMyComparable汎用ではない(タイプ(MyComparable<String>)がない)ため、どちらも機能しません
user85421

1
コード@CarlosHeubergerをどのように入力しているかわからないが、それは私にとって非常にうまく機能し、これが私が探していたものです。
Ivan Perales M.18年

@IvanPeralesM。2か月後...コードをコピーして貼り付け、上記の行を並べ替え行の上にコピーして貼り付けました-これだけです:ideone.com/YNwBbF!上記のコードを正確に入力しましたか?使用してCompartor
user85421

いいえ、ありません。答えの背後にあるアイデアを使用して、関数型をキャストし、コンパイルのタイプとコンパイルの種類を伝えます。
Ivan Perales M.

@IvanPeralesM。さて、それでは私の「タイピング」のあなたの問題は何ですか?投稿されたとおりの回答は機能しません。
user85421

0

あなたはこのようなことを意味しますか?:

<T,S>(T t, S s)->...

このラムダはどのタイプですか?Javaではそれを表現できなかったため、この式を関数アプリケーションで構成することはできず、式は構成可能でなければなりません。

これが機能するためには、JavaのRank2型のサポートが必要です。

メソッドはジェネリックにすることができますが、式として使用することはできません。ただし、渡す前に必要なすべてのジェネリック型を特化することにより、ラムダ式に減らすことができます。ClassName::<TypeName>methodName


1
"Of what type is this lambda? You couldn't express that in Java..."タイプは、他のラムダと同様に、コンテキストを使用して推論されます。ラムダの型は、ラムダ自体で明示的に表現されていません。
Kröw
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.