В этом статья утверждается, что:
one of the major reasons for introducing
defaultmethods in interfaces is to enhance the Collections API in Java 8 to support lambda expressions.
Я мог понять, что @FunctionalInterface помог, сказав, что существует ТОЛЬКО один абстрактный метод, и лямбда должна представлять этот конкретный метод.
Но как получилось, что методы default помогли поддерживать лямбда-выражения?




Они помогли косвенно: вы можете использовать лямбда-выражения в коллекциях благодаря дополнительным методам, таким как 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, не делая любые изменения.
Если вам не нравится реализация интерфейса по умолчанию, вы можете переопределить ее и предоставить свою собственную.
Другая ситуация, когда методы по умолчанию очень помогают, связана с самими функциональными интерфейсами. Возьмем, к примеру, интерфейс Function<T,R>. Единственный метод, который вас действительно интересует, — это R apply(T t), поэтому, когда вам где-то понадобится Function, вы можете передать лямбду, и он создаст экземпляр Function, где этот лямбда-метод будет методом apply.
Однако, когда у вас есть экземпляр Function, вы можете вызывать другие полезные методы, такие как <V> Function<T,V> andThen(Function<? super R,? extends V> after), которые объединяют в них функции. Реализация по умолчанию просто связывает функции в цепочку, но вы можете переопределить ее, если создадите свой собственный класс, реализующий интерфейс Function.
Короче говоря, методы по умолчанию дают вам простой способ создавать лямбда-выражения из функциональных интерфейсов, которые имеют дополнительные методы, и в то же время дают вам возможность переопределить эти дополнительные методы в полном классе, если вам это нужно.
В этом случае абстрактный класс достиг бы того же самого (хотя и убрал бы возможность расширить другой класс).
Я считаю это наиболее важным моментом (по крайней мере, ссылаясь на цитируемое утверждение и заголовок вопроса на уровне языка - не столько на уровне API (коллекции): а именно, что вы можете иметь интерфейс с методами несколько, но все же «реализовать» его с помощью лямбда-выражения тогда и только тогда, когда он имеет один абстрактный метод (фактически, вначале эти типы назывались «типами SAM» или «типами одиночных абстрактных методов»).
Добавление методов по умолчанию в интерфейсы Java, вероятно, тормозило существующий код Java, например: если класс в Java 7 имеет метод int sort() и также реализует Collection, а теперь в Collection есть метод void sort() по умолчанию, то существующий класс больше не будет компилироваться в java 8. Я думаю, что разработчики JDK учли это и выбрали лучший вариант, который у них был на тот момент.