Чем отличается абстрактный класс от интерфейса


Абстрактный класс и интерфейс — это два инструмента объектно-ориентированного программирования, которые позволяют определить контракт (набор методов и свойств), который должен реализовать класс. Несмотря на схожую цель — задать структуру поведения без полной реализации, они различаются по возможностям, применению, правилам наследования и поддержке реализации по умолчанию.

1. Основное различие: цель и семантика

  • Интерфейс описывает только то, что должен делать объект (контракт).

  • Абстрактный класс задаёт и что делать, и как делать — то есть может содержать частичную реализацию.

2. Поддержка множественного наследования

  • Интерфейс: в большинстве языков (Java, C#) можно реализовать несколько интерфейсов.

  • Абстрактный класс: может наследоваться только один (т.к. поддерживается одиночное наследование).

Пример в Java:

interface Flyable { void fly(); }
interface Swimmable { void swim(); }
class Duck implements Flyable, Swimmable {
public void fly() { ... }
public void swim() { ... }
}

3. Наличие реализации

  • Интерфейс (до Java 8): содержит только сигнатуры методов (без тела).

  • Интерфейс (с Java 8): может содержать default-методы и static-методы.

  • Абстрактный класс: может содержать как абстрактные, так и реализованные методы.

abstract class Animal {
void breathe() {
System.out.println("Breathing");
}
abstract void makeSound();
}

4. Состояние (поля, свойства)

  • Интерфейс:

    • До Java 8 — только public static final поля (константы).

    • Нельзя хранить состояние объекта.

  • Абстрактный класс:

    • Может содержать переменные экземпляра.

    • Хранит состояние (например, private int age;).

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

  • Интерфейс: не может иметь конструкторов.

  • Абстрактный класс: может иметь конструкторы, вызываемые при создании экземпляра производного класса.

6. Модификаторы доступа

  • Интерфейс (Java): все методы по умолчанию public.

  • Абстрактный класс: может иметь private, protected, public методы и поля.

7. Наследование поведения

  • Интерфейс: начиная с Java 8, допускается default поведение, но оно не хранит состояние.

  • Абстрактный класс: может наследовать поведение, данные и даже служить "шаблоном".

8. Поддержка свойств и событий (например, в C#)

  • В интерфейсах C# с версии 8.0 тоже появились default interface methods, но они не могут хранить состояние.

  • Абстрактные классы C# полностью поддерживают свойства, события и поля.

9. Назначение

  • Интерфейс:

    • Используется для обеспечения совместимости с разными классами.

    • Идеален для описания ролей, которые могут выполнять объекты.

  • Абстрактный класс:

    • Используется для описания иерархии объектов с частичной реализацией.

    • Удобен при наличии общего состояния и поведения.

10. Примеры из реальной практики

Интерфейс:

interface Drawable {
void draw();
}

Любой класс (Circle, Square, Image) может быть "рисуемым", независимо от их иерархии.

Абстрактный класс:

abstract class Animal {
String name;
void breathe() {
System.out.println(name + " is breathing");
}
abstract void makeSound();
}

Все животные имеют общее поведение (например, дыхание), но издают разные звуки.

11. Производительность

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

  • Однако в современных компиляторах и JIT-интерпретаторах разница часто нивелируется.

12. Модели компиляции и байткода (в JVM)

  • Интерфейсы компилируются в interface-структуры, без таблицы виртуальных методов (vtable).

  • Абстрактные классы компилируются в полноценные классы с vtable.

Это важно для разрешения методов во время выполнения и поддержки полиморфизма.

Таблица сравнения:

Характеристика Интерфейс Абстрактный класс
Поддержка множественного наследования
--- --- ---
Поля Только public static final Любые, включая private
--- --- ---
Методы Абстрактные, default, static Абстрактные и обычные
--- --- ---
Состояние
--- --- ---
Конструкторы
--- --- ---
Использование Контракт (роль) Общая логика и шаблон
--- --- ---
Реализация по умолчанию С Java 8 (default) С самого начала
--- --- ---

Когда выбирать интерфейс, а когда абстрактный класс

  • Интерфейс, если:

    • Не нужно общее состояние.

    • Нужно обеспечить множественную реализацию.

    • Нужно определить поведение, которое реализуется по-разному.

  • Абстрактный класс, если:

    • Есть общее состояние или частичная реализация.

    • Требуется шаблон поведения.

    • Нужно реализовать и поведение, и хранение данных.

Пример (Java)

interface Movable {
void move();
}
abstract class Vehicle {
int speed;
void accelerate() { speed += 10; }
abstract void fuel();
}
class Car extends Vehicle implements Movable {
public void move() { System.out.println("Car is moving"); }
public void fuel() { System.out.println("Filling petrol"); }
}

Здесь Movable — интерфейс, Vehicle — абстрактный класс, а Car реализует контракт и расширяет поведение.

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