Как методы Java 8 по умолчанию помогают с лямбдами?
В этой статье утверждается, что:
Одной из основных причин введения методов по default
в интерфейсах является усовершенствование API коллекций в Java 8 для поддержки лямбда-выражений.
Я мог понять, что @FunctionalInterface
помог, сказав, что существует ТОЛЬКО один абстрактный метод, и лямбда-выражение должно представлять этот конкретный метод.
Но почему методы по default
помогают поддерживать лямбды?
Ответы
Ответ 1
В качестве примера рассмотрим метод 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, не делая любые изменения.
Если вам не нравится реализация по умолчанию в интерфейсе, вы можете переопределить ее и предоставить свою собственную.
Ответ 2
Они помогли косвенно: вы можете использовать лямбда-выражения в коллекциях благодаря дополнительным методам, таким как removeIf()
, stream()
и т.д.
Эти методы не могли быть добавлены в коллекции без полного нарушения существующих реализаций коллекции, если бы они не были добавлены в качестве методов по умолчанию.
Ответ 3
Другая ситуация, когда методы по умолчанию помогают в большей степени, - это сами функциональные интерфейсы. Возьмем Function<T,R>
к примеру, интерфейс Function<T,R>
Единственный метод, который вас действительно волнует, это R apply(T t)
, поэтому, когда вам нужна Function
где-то, вы можете передать лямбду, и она создаст экземпляр Function
где эта лямбда Метод - это метод apply
.
Однако когда у вас есть экземпляр Function
, вы можете вызывать другие полезные методы, такие как <V> Function<T,V> andThen(Function<? super R,? extends V> after)
которые объединяют функции на них. Реализация по умолчанию - это просто цепочка функций, но вы можете переопределить ее, если создадите свой собственный класс, реализующий интерфейс Function
.
Короче говоря, методы по умолчанию дают вам простой способ создания лямбда-выражений из функциональных интерфейсов, имеющих дополнительные методы, и в то же время дают возможность переопределить эти дополнительные методы с помощью полного класса, если это необходимо.
Ответ 4
Хотя это отличный вопрос и уже есть проницательные ответы, но вы должны быть очень осторожны с добавлением метода по default
к любым существующим интерфейсам.
И я беру это из Effective Java - Item 21: Разработка интерфейсов с потомками следующие слова:
Но не всегда возможно написать метод по умолчанию, который поддерживает все инварианты каждой мыслимой реализации.
Хотя добавление метода по умолчанию к существующему интерфейсу облегчает обратную совместимость, но основная цель добавления этого состояла в том, чтобы позволить клиентам упростить использование нового интерфейса без необходимости написания собственной реализации.