Можно ли передать ссылки BiFunction на методы, ожидающие функционального интерфейса?
Я всегда использовал только Java 6 и сейчас пытаюсь узнать, что нового в Java 8. Я читал эту статью здесь: http://www.drdobbs.com/jvm/lambda-expressions-in-java-8/240166764?pgno=2
И это говорит:
Java API определяет несколько универсальных функциональных интерфейсов в пакете java.util.function. Один из интерфейсов, BiFunction, описывает функции с типами параметров T и U и типом возврата R. Вы можете сохранить лямбду сравнения строк в переменной этого типа:
BiFunction comp = (first, second) -> Integer.compare (first.length (), second.length ());
Однако это не поможет вам в сортировке. Нет метода Arrays.sort, который хочет BiFunction. Если вы ранее использовали функциональный язык программирования, это может показаться вам любопытным. Но для Java-программистов это вполне естественно. Интерфейс, такой как Comparator, имеет определенное назначение, а не просто метод с заданным параметром и возвращаемыми типами. Java 8 сохраняет этот вкус. Когда вы хотите что-то сделать с лямбда-выражениями, вам все равно нужно помнить о цели выражения и иметь для этого определенный функциональный интерфейс.
Тем не менее, когда я вижу эту тему: Как вы назначаете лямбда-переменную в Java 8?
Ответы на этот вопрос предлагают делать именно то, что в цитируемом параграфе говорится, что вы не можете делать.
Итак, информация в статье неверна, или я что-то здесь неправильно читаю?
Спасибо!
3 ответа
Я не вижу ничего в связанном SO ответе, что противоречит статье.
Обычные правила системы типов применяются к функциональному интерфейсу.
Если вы объявите переменную как BiFunction<String,String,Integer> bifunc
вы не сможете передать его методу, который требует Comparator<String>
так как BiFunction<String,String,Integer>
не является подтипом Comparator<String>
,
Тот факт, что функциональные типы следуют всем обычным правилам, позволил добавить эту новую функциональность с минимальными возмущениями.
И если вы хотите сделать Comparator
из BiFunction
все, что вам нужно сделать, это добавить ::apply
вот так:
BiFunction<String,String,Integer> bifunc = (a,b) ->
Integer.compare(a.length(), b.length());
Arrays.sort(array, bifunc::apply);
Статья верна в том смысле, что вы не можете сортировать по объекту BiFunction
типа, но вы всегда можете использовать Comparator
, Но эй, у них обоих может быть одно и то же тело. Например:
private static void sort(Comparator<String> ls){
Arrays.sort(someArray, ls);
}
Comparator<String> comp = (String f1, String f2) -> Integer.compare(f1.length(), f2.length());
sort(comp);
BiFunction<String, String, Integer> func = (String f1, String f2) -> Integer.compare(f1.length(), f2.length());
sort((String f1, String f2) -> Integer.compare(f1.length(), f2.length())); //line-4
sort(func) // compiler error
Выше в строке 4 вы можете пройти лямбду, которая точно такая же, как func
, Но вы все еще не можете пройти func
в sort
, Лямбды в java8 являются реализацией некоторого FunctionalInterface. Функциональные интерфейсы получают свой тип на основе своего ссылочного типа. Вот как одна и та же лямбда при инициализации может быть либо BiFunction
или же Comparator
,
Но как только лямбда построена и она получает тип, вы не можете ее изменить. Следовательно, вы не можете пройти func
типа BiFunction
сортировать, что ожидает Comparator
Статья правильная. Вы не можете назначить, например, BiFunction
к Comparator
,
С учетом сказанного, эта замечательная статья, написанная Брайаном Гетцем, хорошо объясняет проблему.
Когда компилятор встречает лямбда-выражение, он сначала понижает (десугирует) лямбда-тело в метод, список аргументов и тип возвращаемого значения которого совпадают с лямбда-выражением
Итак, лямбда может быть обессилена - но что это значит? Ну, в основном это означает, что новый метод (может) быть создан, который так или иначе соответствует lamdba.
class A {
public void foo() {
List<String> list = ...
list.forEach( s -> { System.out.println(s); } );
}
}
Приведенный выше код будет описан примерно так:
class A {
public void foo() {
List<String> list = ...
list.forEach( [lambda for lambda$1 as Consumer] );
}
static void lambda$1(String s) {
System.out.println(s);
}
}
Итак, в случае с BiFunction
а также Comparator
, Предоставленная лямбда может быть назначена как:
// Assign the lambda to a BiFunction
BiFunction<String, String, Integer> b1 =
(first, second) -> Integer.compare(first.length(), second.length());
// Assign the lambda to a Comparator
Comparator<String> c1 =
(first, second) -> Integer.compare(first.length(), second.length());
// But, once the lambda has been assigned to a type you can not reassign it
BiFunction<String, String, Integer> b2 = c1; // <-- Error
Обратите внимание, что как только лямбда был назначен типу (BiFunction
или же Comparator
) тогда он не может быть переназначен, даже если лямбда-выражение совпадает.