Встречается на 56% собеседований по Java

В чём различие между интерфейсом и абстрактным классом

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

Ключевые различия

КритерийИнтерфейсАбстрактный класс
НаследованиеКласс может реализовать несколько интерфейсовКласс может наследовать только один абстрактный класс
МетодыДо Java 8 — только абстрактные; с Java 8 — default и static методы с реализациейМожет содержать как абстрактные, так и обычные методы с реализацией
КонструкторыНе имеет конструкторовМожет иметь конструкторы (вызываются при создании экземпляра подкласса)
ПоляВсе поля по умолчанию public static finalМожет содержать поля с любым модификатором доступа, не обязательно статические или финальные
Модификаторы доступаМетоды по умолчанию public (до Java 9)Методы могут иметь любой модификатор доступа
ИспользованиеОпределение общего контракта для несвязанных классовПредоставление общей реализации для иерархически связанных классов

Как это работает

Интерфейс в Java — это ссылочный тип, который может содержать только абстрактные методы (до Java 8), а также default и static методы с реализацией (начиная с Java 8). Он не может иметь состояние (поля, кроме констант). Класс, реализующий интерфейс, обязан предоставить реализацию всех его абстрактных методов (если класс не абстрактный).

Абстрактный класс — это класс, помеченный ключевым словом abstract. Он может содержать абстрактные методы (без тела) и обычные методы с реализацией. Абстрактный класс не может быть инстанцирован напрямую, но может иметь конструкторы, которые вызываются при создании экземпляра подкласса. Он может содержать поля с любыми модификаторами, включая private, protected и public.

Пример

java
// Интерфейс
interface Drawable {
    void draw(); // абстрактный метод

    default void print() { // default метод с реализацией
        System.out.println("Printing...");
    }
}

// Абстрактный класс
abstract class Shape {
    protected String color;

    public Shape(String color) { // конструктор
        this.color = color;
    }

    public abstract double area(); // абстрактный метод

    public String getColor() { // обычный метод
        return color;
    }
}

// Класс, наследующий абстрактный класс и реализующий интерфейс
class Circle extends Shape implements Drawable {
    private double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }

    @Override
    public void draw() {
        System.out.println("Drawing circle");
    }
}

Подводные камни

  • С Java 8 интерфейсы могут содержать default методы, что позволяет добавлять новые методы в интерфейс без нарушения существующих реализаций. Однако это может привести к проблеме ромбовидного наследования, если класс реализует два интерфейса с одинаковыми default методами — в этом случае класс обязан переопределить конфликтующий метод.
  • Абстрактный класс может иметь конструктор, но он вызывается только через super() в подклассе. Если конструктор абстрактного класса требует параметры, подкласс должен их передать.
  • Поля интерфейса всегда public static final, поэтому они не могут быть изменены и не могут иметь другие модификаторы доступа. Это ограничение важно учитывать при проектировании.
  • Выбор между интерфейсом и абстрактным классом часто зависит от того, нужно ли множественное наследование поведения. Если да — используйте интерфейсы. Если нужна общая реализация и состояние — используйте абстрактный класс.

Когда использовать

  • Используйте интерфейс, когда нужно определить контракт для классов, которые не обязательно связаны иерархически. Например, интерфейс Comparable может быть реализован любым классом, которому нужна сортировка.
  • Используйте абстрактный класс, когда несколько классов имеют общую реализацию и общие поля, и вы хотите избежать дублирования кода. Например, абстрактный класс Animal может содержать поле name и метод eat(), а подклассы Dog и Cat добавляют свои специфические методы.

Коротко

  • Интерфейс — контракт, абстрактный класс — частичная реализация.
  • Множественное наследование: интерфейсы поддерживают, абстрактные классы — нет.
  • Интерфейсы не имеют конструкторов и полей (кроме констант), абстрактные классы могут иметь и то и другое.
  • Выбирайте интерфейс для разнородных классов, абстрактный класс — для схожих с общей логикой.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы