Angenommen, ich habe eine generische Schnittstelle:
interface MyComparable<T extends Comparable<T>> {
public int compare(T obj1, T obj2);
}
Und eine Methode sort
:
public static <T extends Comparable<T>>
void sort(List<T> list, MyComparable<T> comp) {
// sort the list
}
Ich kann diese Methode aufrufen und einen Lambda-Ausdruck als Argument übergeben:
List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));
Das wird gut funktionieren.
Aber jetzt, wenn ich die Schnittstelle nicht generisch und die Methode generisch mache:
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) {
}
Und rufen Sie dann Folgendes auf:
List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));
Es wird nicht kompiliert. Es zeigt einen Fehler beim Lambda-Ausdruck, der sagt:
"Zielmethode ist generisch"
OK, wenn ich es mit kompiliert habe javac
, wird folgender Fehler angezeigt:
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
Aus dieser Fehlermeldung geht hervor, dass der Compiler die Typargumente nicht ableiten kann. Ist das der Fall? Wenn ja, warum passiert das dann so?
Ich habe verschiedene Möglichkeiten ausprobiert und über das Internet gesucht. Dann habe ich diesen JavaCodeGeeks-Artikel gefunden , der einen Weg zeigt, also habe ich versucht:
sort(list, <T extends Comparable<T>>(a, b) -> a.compareTo(b));
was wiederum nicht funktioniert, im Gegensatz zu dem, was dieser Artikel behauptet, dass es funktioniert. Möglicherweise funktionierte es in einigen ersten Builds.
Meine Frage lautet also: Gibt es eine Möglichkeit, einen Lambda-Ausdruck für eine generische Methode zu erstellen? Ich kann dies jedoch mithilfe einer Methodenreferenz tun, indem ich eine Methode erstelle:
public static <T extends Comparable<T>> int compare(T obj1, T obj2) {
return obj1.compareTo(obj2);
}
in einer Klasse sagen SO
und weitergeben als:
sort(list, SO::compare);
Unter Verwendung der Methodenreferenz habe ich einen anderen Weg gefunden, um das Argument zu übergeben:
quelle
Zeigen Sie dem Compiler einfach die richtige Version des generischen Komparators mit
(Comparator<String>)
Die Antwort wird also sein
sort(list, (Comparator<String>)(a, b) -> a.compareTo(b));
quelle
incompatible types: java.util.Comparator<java.lang.String> cannot be converted to MyComparable
undMyComparable
ist nicht generisch (kein Typ),(MyComparable<String>)
würde also auch nicht funktionierenCompartor
?Du meinst so etwas?:
Von welcher Art ist dieses Lambda? Sie konnten dies in Java nicht ausdrücken und können diesen Ausdruck daher nicht in einer Funktionsanwendung zusammenstellen, und Ausdrücke müssen zusammensetzbar sein.
Damit dies funktioniert, benötigen Sie Unterstützung für Rank2-Typen in Java.
Methoden dürfen generisch sein, daher können Sie sie nicht als Ausdrücke verwenden. Sie können jedoch auf den Lambda-Ausdruck reduziert werden, indem Sie alle erforderlichen generischen Typen spezialisieren, bevor Sie sie übergeben können:
ClassName::<TypeName>methodName
quelle
"Of what type is this lambda? You couldn't express that in Java..."
Der Typ würde unter Verwendung des Kontexts abgeleitet, genau wie bei jedem anderen Lambda. Der Typ eines Lambda wird im Lambda selbst nicht explizit ausgedrückt.