Как методы Java 8 по умолчанию помогают с лямбда-выражениями?

В этом статья утверждается, что:

one of the major reasons for introducing default methods in interfaces is to enhance the Collections API in Java 8 to support lambda expressions.

Я мог понять, что @FunctionalInterface помог, сказав, что существует ТОЛЬКО один абстрактный метод, и лямбда должна представлять этот конкретный метод.

Но как получилось, что методы default помогли поддерживать лямбда-выражения?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
25
0
2 411
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Они помогли косвенно: вы можете использовать лямбда-выражения в коллекциях благодаря дополнительным методам, таким как removeIf(), stream() и т. д.

Эти методы нельзя было бы добавить в коллекции без полного нарушения существующих реализаций коллекций, если бы они не были добавлены в качестве методов по умолчанию.

Ответ принят как подходящий

В качестве примера возьмем метод Collection.forEach, который предназначен для использования экземпляра функционального интерфейса Consumer и имеет реализацию по умолчанию в интерфейсе Collection:

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

Если бы разработчики JDK не представили концепцию методов по умолчанию, то все классы, реализующие интерфейс Collection, должны были бы реализовать метод forEach, поэтому было бы проблематично переключиться на Java-8 без нарушения кода.

Таким образом, чтобы облегчить принятие лямбда-выражений и использование новых функциональных интерфейсов, таких как Consumer, Supplier, Predicate и т. д., разработчики JDK представили концепцию методов по умолчанию для обеспечения обратной совместимости, и теперь проще переключиться на Java-8, не делая любые изменения.

Если вам не нравится реализация интерфейса по умолчанию, вы можете переопределить ее и предоставить свою собственную.

Добавление методов по умолчанию в интерфейсы Java, вероятно, тормозило существующий код Java, например: если класс в Java 7 имеет метод int sort() и также реализует Collection, а теперь в Collection есть метод void sort() по умолчанию, то существующий класс больше не будет компилироваться в java 8. Я думаю, что разработчики JDK учли это и выбрали лучший вариант, который у них был на тот момент.

dorony 26.04.2019 19:35

Другая ситуация, когда методы по умолчанию очень помогают, связана с самими функциональными интерфейсами. Возьмем, к примеру, интерфейс Function<T,R>. Единственный метод, который вас действительно интересует, — это R apply(T t), поэтому, когда вам где-то понадобится Function, вы можете передать лямбду, и он создаст экземпляр Function, где этот лямбда-метод будет методом apply.

Однако, когда у вас есть экземпляр Function, вы можете вызывать другие полезные методы, такие как <V> Function<T,V> andThen(Function<? super R,? extends V> after), которые объединяют в них функции. Реализация по умолчанию просто связывает функции в цепочку, но вы можете переопределить ее, если создадите свой собственный класс, реализующий интерфейс Function.

Короче говоря, методы по умолчанию дают вам простой способ создавать лямбда-выражения из функциональных интерфейсов, которые имеют дополнительные методы, и в то же время дают вам возможность переопределить эти дополнительные методы в полном классе, если вам это нужно.

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

OrangeDog 22.04.2019 11:04

Я считаю это наиболее важным моментом (по крайней мере, ссылаясь на цитируемое утверждение и заголовок вопроса на уровне языка - не столько на уровне API (коллекции): а именно, что вы можете иметь интерфейс с методами несколько, но все же «реализовать» его с помощью лямбда-выражения тогда и только тогда, когда он имеет один абстрактный метод (фактически, вначале эти типы назывались «типами SAM» или «типами одиночных абстрактных методов»).

Marco13 22.04.2019 20:07

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