注:この質問は前のSO質問であったリンク切れから生じていますが、ここでは...
このコードを参照してください(注:このコードは「機能しない」こと、およびInteger::compare
を使用する必要があることはわかっています - リンクした質問から抽出しただけです。
final ArrayList <Integer> list
= IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());
System.out.println(list.stream().max(Integer::max).get());
System.out.println(list.stream().min(Integer::min).get());
.min()
と .max()
のJavadocによると、両方の引数はComparator
になります。それでもここでのメソッド参照は Integer
クラスの静的メソッドです。
それで、なぜこれはまったくコンパイルしないのでしょうか。
明らかではないので、ここで何が起こっているのか説明させてください。
まず、 Stream.max()
は Comparator
のインスタンスを受け取るので、ストリーム内の項目は次のようになります。あなたはあまり気にする必要はありませんいくつかの最適な順序で、最小値または最大値を見つけるために互いに比較することができます。
それで、質問は、もちろん、なぜ Integer::max
が受け入れられるのですか?結局のところ、それはコンパレータではありません!
その答えは、新しいラムダ機能がJava 8で機能するという点にあります。それは、「単一の抽象メソッド」インターフェース、または「SAM」インターフェースとして非公式に知られている概念に依存しています。そのアイデアは、1つの抽象メソッドを持つすべてのインタフェースは、そのメソッドシグネチャがそのインタフェース上の1つのメソッドに一致するすべてのラムダ(またはメソッド参照)によって自動的に実装できるということです。そこで、 Comparator
インターフェース(単純版)を調べます。
public Comparator<T> {
T compare(T o1, T o2);
}
メソッドがComparator<Integer>
を探しているなら、それは本質的にこのシグネチャを探しています:
int xxx(Integer o1, Integer o2);
"xxx"を使用しています - メソッド名は照合の目的では使用されていないため 。
したがって、Integer.min(int a, int b)
とInteger.max(int a, int b)
はどちらも十分に近いため、オートボクシングによってメソッドコンテキストでこれをComparator<Integer>
として表示することができます。
Comparator
は機能的なインターフェースであり、Integer::max
はそのインターフェースに準拠しています(自動ボクシング/ボックス解除が考慮された後)。これは2つのint
値を取り、int
を返します。これは、Comparator<Integer>
がtoと予想しているのと同じです(これも、Integer/intの違いを無視するためのものです)。
しかし、Integer.max
がComparator.compare
の意味に準拠していないことを考えると、正しいことをするとは思わないでしょう。そして実際、それは本当に一般的には機能しません。たとえば、1つ小さな変更を加えます。
for (int i = 1; i <= 20; i++)
list.add(-i);
... max
の値は-20、min
の値は-1です。
代わりに、両方の呼び出しでInteger::compare
を使用する必要があります。
System.out.println(list.stream().max(Integer::compare).get());
System.out.println(list.stream().min(Integer::compare).get());
Integer::min
はComparator<Integer>
インターフェースの実装に解決されるため、これは機能します。
Integer::min
のメソッド参照はInteger.min(int a, int b)
に解決され、IntBinaryOperator
に解決され、おそらくオートボクシングがどこかで発生してBinaryOperator<Integer>
になります。
そしてStream<Integer>
のmin()
またはmax()
メソッドは、Comparator<Integer>
インターフェースの実装を要求します。
これで、これは単一のメソッドInteger compareTo(Integer o1, Integer o2)
に解決されます。これはBinaryOperator<Integer>
型です。
そして、両方の方法がBinaryOperator<Integer>
であるため、魔法が起こりました。
David M. Lloydによる情報とは別に、これを可能にするメカニズムはターゲットタイピングと呼ばれることがあります。
その考え方は、コンパイラーがラムダ式またはメソッド参照に割り当てる型は、式自体だけでなく、それが使用される場所にも依存するということです。
式のターゲットは、結果が割り当てられる変数、または結果が渡されるパラメータです。
ラムダ式とメソッド参照には、その型が見つかると、そのターゲットの型と一致する型が割り当てられます。
詳細については、Javaチュートリアルの 型推論セクション を参照してください。
配列が最大値と最小値を取得するときにエラーが発生したので、次のようにしました。
int max = Arrays.stream(arrayWithInts).max().getAsInt();
int min = Arrays.stream(arrayWithInts).min().getAsInt();