Ответ
Модификатор default (метод по умолчанию) позволяет добавлять в интерфейсы Java методы с реализацией, не ломая существующие классы, которые этот интерфейс уже реализуют. Введены в Java 8 для эволюции API.
Основная цель: Обеспечить обратную совместимость при добавлении новой функциональности в интерфейсы.
Синтаксис и пример:
interface Vehicle {
// Обычный абстрактный метод
String getBrand();
// Метод по умолчанию - имеет реализацию
default String startEngine() {
return "Engine of " + getBrand() + " started.";
}
// Статический метод в интерфейсе (также с Java 8)
static void honk() {
System.out.println("Beep!");
}
}
class Car implements Vehicle {
@Override
public String getBrand() {
return "Toyota";
}
// Метод startEngine() НЕ ОБЯЗАТЕЛЬНО переопределять,
// будет использована реализация по умолчанию.
}
// Использование
Car myCar = new Car();
System.out.println(myCar.startEngine()); // "Engine of Toyota started."
Vehicle.honk(); // Вызов статического метода
Ключевые правила и особенности:
- Не обязателен для переопределения: Классы, реализующие интерфейс, могут использовать реализацию по умолчанию, не переопределяя метод.
- Конфликт при множественном наследовании: Если класс реализует два интерфейса с одинаковыми
default-методами, возникает неоднозначность. Класс обязан явно переопределить этот метод.interface A { default void foo() { System.out.println("A"); } } interface B { default void foo() { System.out.println("B"); } } class C implements A, B { @Override public void foo() { // Можно выбрать одну из реализаций или предоставить свою A.super.foo(); // Явный вызов реализации из интерфейса A } } - Не могут переопределять методы
Object: Интерфейс не может объявитьdefault-метод дляequals,hashCodeилиtoString. - Модификаторы доступа:
default-методы неявноpublic. Их нельзя сделатьprivate,protectedилиstatic(дляstaticесть отдельный вид методов). - Отличие от абстрактных классов: Интерфейс с
default-методами всё ещё не может иметь состояния (полей экземпляра), в отличие от абстрактного класса.
Типичное применение: Добавление удобных методов-утилит в интерфейсы коллекций (например, sort() в List<E>), что позволило улучшить API, не требуя изменений в миллионах строк существующего кода.
Ответ 18+ 🔞
Ну, слушай, вот есть у нас в Java интерфейсы, да? Классика жанра: нагородишь там кучу абстрактных методов, и потом все, кто их реализует, обязаны каждый из них прописать. А если через пять лет захотелось добавить в этот интерфейс ещё одну фичу — новую методяру? Так всех, кто его использует, придётся ломать и заставлять её реализовывать. Овердохуища работы, пиздец.
Вот чтобы такого не было, в Java 8 ввели, блядь, модификатор default. Гениальная, сука, идея! Теперь можно в интерфейс пихнуть метод с готовой реализацией, и все старые классы, которые этот интерфейс уже юзают, даже не чихнут — они просто получат этот метод в подарок, как есть. Обратная совместимость наше всё, ёпта!
Смотри, как это выглядит, на примере:
interface Vehicle {
// Ну это обычный, старый добрый абстрактный метод. Без тела.
String getBrand();
// А ВОТ ЭТО УЖЕ НОВАЯ ФИШКА! Метод с реализацией прямо в интерфейсе.
default String startEngine() {
return "Engine of " + getBrand() + " started.";
}
// Заодно и статические методы в интерфейсы запихнули, кстати. Тоже с 8-й версии.
static void honk() {
System.out.println("Beep!");
}
}
class Car implements Vehicle {
@Override
public String getBrand() {
return "Toyota";
}
// И смотри, сука, в чём магия: метод startEngine() мне тут ПЕРЕОПРЕДЕЛЯТЬ НЕ НАДО!
// Он уже работает, берёт мою getBrand() и подставляет. Красота!
}
// Ну и пользуемся:
Car myCar = new Car();
System.out.println(myCar.startEngine()); // Выведет: "Engine of Toyota started."
Vehicle.honk(); // А это статический метод, вызывается прямо от имени интерфейса.
Вот так вот, в рот меня чих-пых! Удобно же?
Но, как всегда, есть свои подводные, блядь, камни. Главный — конфликты при множественном наследовании. Представь, твой класс реализует два интерфейса, и в обоих есть default-метод с одинаковым именем. Компилятор сходит с ума: "Бля, а какую из двух реализаций-то брать?!" И он тебя заставит этот конфликт разрешить вручную.
interface A { default void foo() { System.out.println("A"); } }
interface B { default void foo() { System.out.println("B"); } }
class C implements A, B {
@Override
public void foo() {
// Компилятор говорит: "Чувак, решай сам, нахуй! Или свою пиши, или явно укажи, какую из родительских хочешь."
A.super.foo(); // Вот так можно явно вызвать реализацию из интерфейса A. Да, синтаксис тот ещё, ёперный театр.
}
}
Ещё важные моменты, чтобы не облажаться:
- Нельзя трогать методы
Object. Не вздумай объявитьdefault boolean equals(Object o). Компилятор тебе такого не позволит, и правильно сделает. - Все
default-методы —public. Даже если не написать, они такие по умолчанию. Сделать ихprivateилиprotected— нихуя. - Это не абстрактный класс! Состояния (поля экземпляра) в интерфейс всё равно не запихнёшь. Только константы (
public static final). Так что не путай.
Где это пригодилось? Да везде! Вот, например, в интерфейс List<E> добавили кучу удобных default-методов вроде sort() или replaceAll(). И все твои ArrayList, LinkedList и прочие коллекции моментально стали умнее, без единого изменения в их исходниках. Вот это и есть эволюция API без боли, блядь!
Короче, штука мощная. Главное — использовать с умом и не создавать лишних конфликтов, а то сам потом в них и запутаешься.