Лямбда-выражение и общий метод


111

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

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);

Ответы:


117

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

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

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

47
Однако это ограничение не распространяется на ссылки на универсальные методы. Вы можете использовать ссылку на универсальный метод с универсальным функциональным интерфейсом.
Брайан Гетц

17
Я уверен, что для этого ограничения есть веская причина. Что это?
Sandro

6
@Sandro: просто нет синтаксиса для объявления параметров типа лямбда-выражения. И такой синтаксис был бы очень сложным. Имейте в виду, что синтаксический анализатор по-прежнему должен иметь возможность определять такое лямбда-выражение с параметрами типа отдельно от других допустимых конструкций Java. Таким образом, вам придется прибегать к ссылкам на методы. Целевой метод может объявлять параметры типа, используя установленный синтаксис.
Хольгер

2
@Holger по-прежнему, где параметры типа могут быть выведены автоматически, компилятор может распознать тип захвата, как это происходит, когда вы объявляете, например, Set <?>, И выполняете проверки типов с этими захваченными типами. Конечно, это делает невозможным их
WorldSEnder

17

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

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

3

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

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

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


2
incompatible types: java.util.Comparator<java.lang.String> cannot be converted to MyComparableи MyComparableне является универсальным (без типа), поэтому (MyComparable<String>)тоже не будет работать
user85421

1
Не знаю, как вы набираете код @CarlosHeuberger, но у меня он работает очень хорошо, это то, что я искал.
Иван Пералес М.

@IvanPeralesM. через 2 месяца ... Я скопировал и вставил ваш код и скопировал и вставил строку выше над строкой сортировки - вот так, как здесь: ideone.com/YNwBbF ! Вы уверены, что ввели указанный выше код в точности? используя Compartor?
user85421

Нет, я использую идею, лежащую в основе ответа, чтобы использовать функционал, чтобы сообщить компиляции, какой это тип, и он сработал.
Иван Пералес М.

@IvanPeralesM. ну тогда в чем твоя проблема с моим "набором текста"? Ответ, как он опубликован, не работает.
user85421

0

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

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

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

Для этого вам понадобится поддержка типов Rank2 в Java.

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


1
"Of what type is this lambda? You couldn't express that in Java..."Тип будет выводиться с использованием контекста, как и для любой другой лямбды. Тип лямбды явно не выражен в самой лямбде.
Kröw
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.