Hinweis: Diese Frage stammt von einem toten Link, der eine vorherige SO-Frage war, aber hier geht ...
Siehe diesen Code ( Hinweis: Ich weiß, dass dieser Code nicht "funktioniert" und Integer::compare
verwendet werden sollte - ich habe ihn gerade aus der verknüpften Frage extrahiert ):
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());
Nach dem Javadoc von .min()
und .max()
sollte das Argument von beiden a sein Comparator
. Hier beziehen sich die Methodenreferenzen jedoch auf statische Methoden der Integer
Klasse.
Warum wird das überhaupt kompiliert?
java
java-8
java-stream
fge
quelle
quelle
Integer::compare
anstelle vonInteger::max
und verwendet werden sollteInteger::min
.Integer
keine Methoden von sindComparator
.Antworten:
Lassen Sie mich erklären, was hier passiert, denn es ist nicht offensichtlich!
Erstens
Stream.max()
nimmt eine InstanzComparator
so , dass Gegenstände in dem Strom können gegeneinander verglichen werden , das Minimum oder Maximum zu finden, die in einem gewissen optimalen Reihenfolge , dass Sie nicht zu viel Sorgen zu machen brauchen.Die Frage ist also natürlich, warum
Integer::max
akzeptiert wird? Immerhin ist es kein Komparator!Die Antwort liegt in der Art und Weise, wie die neue Lambda-Funktionalität in Java 8 funktioniert. Sie basiert auf einem Konzept, das informell als "Single Abstract Method" -Schnittstellen oder "SAM" -Schnittstellen bezeichnet wird. Die Idee ist, dass jede Schnittstelle mit einer abstrakten Methode automatisch von jedem Lambda oder jeder Methodenreferenz implementiert werden kann, deren Methodensignatur mit der einen Methode auf der Schnittstelle übereinstimmt. Untersuchen Sie also die
Comparator
Schnittstelle (einfache Version):Wenn eine Methode nach a sucht
Comparator<Integer>
, sucht sie im Wesentlichen nach dieser Signatur:Ich verwende "xxx", weil der Methodenname nicht für Übereinstimmungszwecke verwendet wird .
Daher sind beide
Integer.min(int a, int b)
undInteger.max(int a, int b)
nahe genug, dass Autoboxing dies alsComparator<Integer>
in einem Methodenkontext erscheinen lässt.quelle
list.stream().mapToInt(i -> i).max().get()
..getAsInt()
stattdessen verwendenget()
, da Sie es mit einem zu tun habenOptionalInt
.max()
Funktion bereitzustellen !Comparator
wir anhand der Dokumentation sehen können, dass sie mit der Anmerkung versehen ist@FunctionalInterface
. Dieser Dekorateur ist die Magie, die es ermöglichtInteger::max
undInteger::min
in eine umgewandelt werden kannComparator
.@FunctionalInterface
dient hauptsächlich zu Dokumentationszwecken, da der Compiler dies problemlos mit jeder Schnittstelle mit einer einzigen abstrakten Methode tun kann.Comparator
ist eine funktionale Schnittstelle undInteger::max
entspricht dieser Schnittstelle (nachdem Autoboxing / Unboxing berücksichtigt wurde). Es nimmt zweiint
Werte an und gibt ein zurückint
- genau wie Sie es erwarten würdenComparator<Integer>
(erneut Schielen, um den Unterschied zwischen Ganzzahl und Int zu ignorieren).Ich würde jedoch nicht erwarten, dass es das Richtige tut, da
Integer.max
dies nicht der Semantik von entsprichtComparator.compare
. Und tatsächlich funktioniert es im Allgemeinen nicht wirklich. Nehmen Sie zum Beispiel eine kleine Änderung vor:... und jetzt ist der
max
Wert -20 und dermin
Wert -1.Stattdessen sollten beide Aufrufe Folgendes verwenden
Integer::compare
:quelle
Comparator<Integer>
hätteint compare(Integer, Integer)
... es ist nicht umwerfend, dass Java eine Methodenreferenz zulässtint max(int, int)
, um darauf zu konvertieren ...Integer::max
? Aus seiner Sicht haben Sie eine Funktion übergeben, die ihrer Spezifikation entspricht. Das ist alles, was sie wirklich tun kann.Comparator.compare
. Es sollte einenum
von zurückgeben{LessThan, GreaterThan, Equal}
, keinint
. Auf diese Weise würde die Funktionsschnittstelle nicht wirklich übereinstimmen und Sie würden einen Kompilierungsfehler erhalten. IOW: Die Typensignatur vonComparator.compare
erfasst die Semantik dessen, was es bedeutet, zwei Objekte zu vergleichen, nicht angemessen, und daher haben andere Schnittstellen, die absolut nichts mit dem versehentlichen Vergleichen von Objekten zu tun haben, dieselbe Typensignatur.Dies funktioniert, weil
Integer::min
eine Implementierung derComparator<Integer>
Schnittstelle aufgelöst wird.Die Methodenreferenz von
Integer::min
AuflösenInteger.min(int a, int b)
, AuflösenIntBinaryOperator
und vermutlich Autoboxen tritt irgendwo auf, wodurch es zu einem wirdBinaryOperator<Integer>
.Und die
min()
resp-max()
Methoden derStream<Integer>
fordern dieComparator<Integer>
zu implementierende Schnittstelle auf.Dies wird nun auf die einzelne Methode aufgelöst
Integer compareTo(Integer o1, Integer o2)
. Welches ist vom TypBinaryOperator<Integer>
.Und so ist die Magie passiert, da beide Methoden a sind
BinaryOperator<Integer>
.quelle
Integer::min
implementiertComparable
. Es ist kein Typ, der irgendetwas implementieren kann. Aber es wird zu einem Objekt ausgewertet, das implementiertComparable
.Comparator<Integer>
ist eine Single-Abstract-Method-Schnittstelle (auch als "funktional" bezeichnet) undInteger::min
erfüllt ihren Vertrag, sodass das Lambda so interpretiert werden kann. Ich weiß nicht, wie BinaryOperator hier ins Spiel kommt (oder auch IntBinaryOperator) - es gibt keine Subtypisierungsbeziehung zwischen diesem und Comparator.Abgesehen von den Informationen von David M. Lloyd könnte man hinzufügen, dass der Mechanismus, der dies ermöglicht, als Zieltypisierung bezeichnet wird .
Die Idee ist, dass der Typ, den der Compiler einem Lambda-Ausdruck oder einer Methodenreferenz zuweist, nicht nur vom Ausdruck selbst abhängt, sondern auch davon, wo er verwendet wird.
Das Ziel eines Ausdrucks ist die Variable, der sein Ergebnis zugewiesen ist, oder der Parameter, an den sein Ergebnis übergeben wird.
Lambda-Ausdrücken und Methodenreferenzen wird ein Typ zugewiesen, der dem Typ ihres Ziels entspricht, sofern ein solcher Typ gefunden werden kann.
Siehe die Type Inference Abschnitt in den Java - Tutorial für weitere Informationen.
quelle
Ich hatte einen Fehler mit einem Array, das das Maximum und das Minimum erhielt. Meine Lösung war also:
quelle