Lambda-expressie en generiek alleen gedefinieerd in methode

Stel dat ik een algemene interface heb:

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

En een methode sort:

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

Ik kan deze methode aanroepen en een lambda-expressie als argument doorgeven:

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

Dat zal prima werken.

Maar als ik nu de interface niet-generiek maak, en de methode generiek:

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) {
}

En roep dit dan aan als:

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

Het compileert niet. Het toont een fout bij de lambda-expressie die zegt:

“Doelmethode is generiek”

OK, toen ik het compileerde met behulp van javac, vertoonde het de volgende fout:

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

Uit deze foutmelding lijkt het alsof de compiler de typeargumenten niet kan afleiden. Is dat het geval? Zo ja, waarom gebeurt het dan zo?

Ik heb verschillende manieren geprobeerd, gezocht via internet. Toen vond ik dit JavaCodeGeeks-artikel, waarin een manier, dus ik probeerde:

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

wat weer niet werkt, in tegenstelling tot wat dat artikel beweert dat het werkt. Het is mogelijk dat het in sommige initiële builds werkte.

Dus mijn vraag is: Is er een manier om lambda-expressie te creëren voor een generieke methode? Ik kan dit echter doen met behulp van een methodereferentie, door een methode te maken:

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

zeg in een klas SOen geef het door als:

sort(list, SO::compare);

Antwoord 1, autoriteit 100%

U kunt geen lambda-expressiegebruiken voor een functionele interface, als de methode in de functionele interfacetypeparameters heeft . Zie sectie §15.27.3 in JLS8:

Een lambda-expressie is compatibel [..] met een doeltype Tals Teen functioneel interfacetype is (§9.8) en de expressie congruent is met het functietype van [..] T. [..] Een lambda-expressie is congruentmet een functietype als al het volgende is
waar:

  • Het functietype heeft geen typeparameters.
  • [..]

Antwoord 2, autoriteit 16%

Met behulp van methodeverwijzing vond ik een andere manier om het argument door te geven:

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

Antwoord 3, autoriteit 2%

Wijs de compiler gewoon naar de juiste versie van de generieke Comparator met
(Comparator<String>)

Dus het antwoord is

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


Antwoord 4

Bedoel je zoiets?:

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

Van welk type is deze lambda? Je zou dat niet kunnen uitdrukken in Java en daarom kun je deze expressie niet samenstellen in een functietoepassing en expressies moeten composeerbaar zijn.

Om dit werk te kunnen doen, heb je ondersteuning nodig voor Rank2-typenin Java.

Methoden mogen generiek zijn, maar daarom kun je ze niet als uitdrukkingen gebruiken. Ze kunnen echter worden teruggebracht tot lambda-expressie door alle noodzakelijke generieke typen te specialiseren voordat u ze kunt doorgeven: ClassName::<TypeName>methodName


Antwoord 5

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

De int compareTo (T o)is geen algemene methodeaanroep. Hoewel Comparable<T>een interface is met een type. Zelfs als compareToThad geretourneerd, d.w.z. T compareTo (T o), is het nog steeds geen generieke methode. Om een ​​generieke methode te zijn, moet het een lijst met type parametersbevatten, d.w.z. <T> T compareTo (T o).

Other episodes