it-swarm.com.ru

Лямбда-выражение и универсальный метод

Предположим, у меня есть общий интерфейс:

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

И метод sort:

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

Я могу вызвать этот метод и передать лямбда-выражение в качестве аргумента:

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

Это будет работать нормально. 

Но теперь, если я сделаю интерфейс не универсальным, а метод универсальным:

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

И затем вызвать это как:

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

Это не компилируется. Это показывает ошибку в лямбда-выражении, говорящем: 

«Целевой метод является общим»

Хорошо, когда я скомпилировал его с помощью javac, он показывает следующую ошибку:

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

Из этого сообщения об ошибке кажется, что компилятор не может вывести аргументы типа. Это тот случай? Если да, то почему это происходит так?

Я пробовал разные способы, искал через интернет. Затем я нашел эту статью JavaCodeGeeks , которая показывает путь, поэтому я попытался:

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

что опять не работает, вопреки тому, что эта статья утверждает, что она работает. Возможно, раньше он работал в некоторых начальных сборках.

Итак, мой вопрос: есть ли способ создать лямбда-выражение для универсального метода? Я могу сделать это, используя ссылку на метод, создав метод:

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

в каком-то классе произнесите SO и передайте его как:

sort(list, SO::compare);
97
Rohit Jain

Вы не можете использовать лямбда-выражение для функциональный интерфейс, если метод в функциональный интерфейс имеет параметры типа. Смотрите раздел §15.27.3 в JLS8 :

Лямбда-выражение совместимо [..] с целевым типом T, если T является типом функционального интерфейса (§9.8), а выражение является congruent с типом функции [..] T. [..] Лямбда-выражением является congruent с типом функции, если все перечисленные ниже правда:

  • Тип функции имеет без параметров типа.
  • [..]
98
nosid

Используя ссылку на метод, я нашел другой способ передать аргумент:

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

Просто укажите компилятору правильную версию универсального компаратора с помощью (Comparator<String>)

Так что ответ будет 

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

1
Aleч

Вы имеете в виду что-то вроде этого?:

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

К какому типу относится эта лямбда? Вы не можете выразить это в Java и, следовательно, не можете составить это выражение в приложении-функции, а выражения должны быть компонуемыми.

Чтобы это работало, вам понадобится поддержка Типы Rank2 в Java.

Методы могут быть универсальными, но поэтому вы не можете использовать их как выражения. Однако их можно свести к лямбда-выражению, специализировав все необходимые универсальные типы, прежде чем вы сможете передать их: ClassName::<TypeName>methodName 

0
iconfly