Лямбда-експресія та родовий метод


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), а вираз відповідає конфігурації типу функції [..] Т. [..] Вираз лямбда відповідає з типом функції, якщо всі наведені нижче дії є істинними:

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

47
Однак це обмеження не поширюється на посилання методів на загальні методи. Можна використовувати посилання на метод на загальний метод із загальним функціональним інтерфейсом.
Брайан Гец

17
Я впевнений, що є вагома причина цього обмеження. Що це?
Сандро

6
@Sandro: просто немає синтаксису для оголошення параметрів типу для лямбда-виразу. І такий синтаксис був би дуже складним. Майте на увазі, що аналізатор все-таки повинен бути в змозі сказати такий вираз лямбда з параметрами типу, крім інших законних конструкцій Java. Таким чином, ви повинні вдатися до посилань на методи. Цільовий метод може оголосити параметри типу за допомогою встановленого синтаксису.
Холгер

2
@Holger все-таки, де параметри типу можна автоматично виводити, компілятор може розшифровувати тип capure, як це робиться, коли ви декларуєте, наприклад, 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?
користувач85421

Ні, я не використовую, я використовую ідею, що стоїть за відповіддю, щоб подати функціонал, щоб сказати компілятору, що це за тип і він працював.
Іван Пералес М.

@IvanPeralesM. ну, тоді яка у вас проблема з моїм "набором тексту"? Відповідь, як вона розміщена, не працює.
користувач85421

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.