Можно ли передать ссылки 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) тогда он не может быть переназначен, даже если лямбда-выражение совпадает.

Другие вопросы по тегам