Перейти к основному содержимому

Интерфейс с методами по умолчанию против абстрактного класса

· 4 мин. чтения

1. Введение

После введения методов по умолчанию в интерфейсы Java казалось, что больше нет никакой разницы между интерфейсом и абстрактным классом. Но это не так — между ними есть некоторые принципиальные различия.

В этом уроке мы более подробно рассмотрим интерфейс и абстрактный класс, чтобы увидеть, чем они отличаются.

2. Зачем использовать метод по умолчанию?

Цель метода по умолчанию — предоставить внешние функции без нарушения существующих реализаций. Первоначальной причиной введения метода по умолчанию было обеспечение обратной совместимости с Collection Framework с новыми лямбда-функциями.

3. Интерфейс с методом по умолчанию против абстрактного класса

Рассмотрим основные принципиальные отличия.

3.1. Состояние

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

Любая логика, которую мы пишем в методе по умолчанию , должна относиться к другим методам интерфейса — эти методы не будут зависеть от состояния объекта .

Допустим, мы создали абстрактный класс CircleClass , который содержит String , color , для представления состояния объекта CircleClass :

public abstract class CircleClass {

private String color;
private List<String> allowedColors = Arrays.asList("RED", "GREEN", "BLUE");

public boolean isValid() {
if (allowedColors.contains(getColor())) {
return true;
} else {
return false;
}
}

//standard getters and setters
}

В приведенном выше абстрактном классе у нас есть неабстрактный метод isValid() для проверки объекта CircleClass на основе его состояния. Метод isValid() может получить доступ к состоянию объекта CircleClass и проверить экземпляр CircleClass на основе разрешенных цветов. Благодаря такому поведению мы можем написать любую логику в методе абстрактного класса на основе состояния объекта .

Давайте создадим простой класс реализации CircleClass :

public class ChildCircleClass extends CircleClass {
}

Теперь давайте создадим экземпляр и проверим цвет:

CircleClass redCircle = new ChildCircleClass();
redCircle.setColor("RED");
assertTrue(redCircle.isValid());

Здесь мы видим, что когда мы помещаем допустимый цвет в объект CircleClass и вызываем метод isValid() , внутри метод isValid() может получить доступ к состоянию объекта CircleClass и проверить, содержит ли экземпляр допустимый цвет или нет. .

Попробуем сделать нечто подобное, используя интерфейс с методом по умолчанию :

public interface CircleInterface {
List<String> allowedColors = Arrays.asList("RED", "GREEN", "BLUE");

String getColor();

public default boolean isValid() {
if (allowedColors.contains(getColor())) {
return true;
} else {
return false;
}
}
}

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

Здесь мы определили метод getColor() для предоставления информации о состоянии. Дочерний класс переопределит метод getColor() , чтобы предоставить состояние экземпляра во время выполнения:

public class ChidlCircleInterfaceImpl implements CircleInterface {
private String color;

@Override
public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}
}

Давайте создадим экземпляр и проверим цвет:

ChidlCircleInterfaceImpl redCircleWithoutState = new ChidlCircleInterfaceImpl();
redCircleWithoutState.setColor("RED");
assertTrue(redCircleWithoutState.isValid());

Как мы видим здесь, мы переопределяем метод getColor() в дочернем классе, чтобы метод по умолчанию проверял состояние во время выполнения.

3.2. Конструкторы

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

3.3. Синтаксические различия

Кроме того, есть несколько различий в синтаксисе. Абстрактный класс может переопределить методы класса Object , а интерфейс — нет.

Абстрактный класс может объявлять переменные экземпляра со всеми возможными модификаторами доступа, и к ним можно получить доступ в дочерних классах. Интерфейс может иметь только переменные public, static и final и не может иметь никаких переменных экземпляра.

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

Наконец, абстрактный класс не может ссылаться на лямбда-выражение , а интерфейс может иметь единственный абстрактный метод, который может ссылаться на лямбда-выражение.

4. Вывод

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

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

Как обычно, все примеры кода, показанные в этой статье, доступны на GitHub .