Załóżmy, że mam ogólny interfejs:
interface MyComparable<T extends Comparable<T>> {
public int compare(T obj1, T obj2);
}
I metoda sort
:
public static <T extends Comparable<T>>
void sort(List<T> list, MyComparable<T> comp) {
// sort the list
}
Mogę wywołać tę metodę i przekazać jako argument wyrażenie lambda:
List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));
To zadziała dobrze.
Ale teraz, jeśli sprawię, że interfejs nie będzie generyczny, a metoda generyczna:
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) {
}
A następnie wywołaj to w ten sposób:
List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));
Nie kompiluje się. Pokazuje błąd w wyrażeniu lambda mówiącym:
„Metoda docelowa jest ogólna”
OK, kiedy skompilowałem go za pomocą javac
, pokazuje następujący błąd:
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
Na podstawie tego komunikatu o błędzie wydaje się, że kompilator nie jest w stanie wywnioskować argumentów typu. Czy tak jest? Jeśli tak, to dlaczego tak się dzieje?
Próbowałem różnych sposobów, szukałem w Internecie. Potem znalazłem ten artykuł JavaCodeGeeks , który pokazuje sposób, więc spróbowałem:
sort(list, <T extends Comparable<T>>(a, b) -> a.compareTo(b));
co znowu nie działa, w przeciwieństwie do tego, co twierdzi ten artykuł, że działa. Możliwe, że działał w niektórych początkowych wersjach.
Moje pytanie brzmi: czy istnieje sposób na utworzenie wyrażenia lambda dla metody ogólnej? Mogę to jednak zrobić za pomocą odwołania do metody, tworząc metodę:
public static <T extends Comparable<T>> int compare(T obj1, T obj2) {
return obj1.compareTo(obj2);
}
w jakiejś klasie powiedz SO
i przekaż to jako:
sort(list, SO::compare);
Korzystając z odwołania do metody, znalazłem inny sposób przekazania argumentu:
źródło
Po prostu wskaż kompilatorowi odpowiednią wersję ogólnego Komparatora z
(Comparator<String>)
Więc odpowiedź będzie
sort(list, (Comparator<String>)(a, b) -> a.compareTo(b));
źródło
incompatible types: java.util.Comparator<java.lang.String> cannot be converted to MyComparable
iMyComparable
nie jest ogólny (brak typu), więc też(MyComparable<String>)
nie zadziałaCompartor
?Masz na myśli coś takiego ?:
Jakiego typu jest ta lambda? Nie można tego wyrazić w Javie i dlatego nie można skomponować tego wyrażenia w aplikacji funkcji, a wyrażenia muszą być składowalne.
Do tego potrzeba by było wsparcie dla typów Rank2 w Javie.
Metody mogą być ogólne, ale w związku z tym nie można ich używać jako wyrażeń. Można je jednak zredukować do wyrażenia lambda, specjalizując wszystkie niezbędne typy ogólne, zanim będzie można je przekazać:
ClassName::<TypeName>methodName
źródło
"Of what type is this lambda? You couldn't express that in Java..."
Typ zostałby wywnioskowany przy użyciu kontekstu, tak jak w przypadku każdej innej lambdy. Typ lambda nie jest jawnie wyrażony w samej lambdzie.