← Назад к вопросам

В чём разница между Abstract class и Interface? Когда какой лучше использовать?

1.0 Junior🔥 211 комментариев
#Kotlin основы#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Отличие Abstract Class (Абстрактного Класса) и Interface (Интерфейса)

Основные концептуальные различия

Абстрактный класс — это класс, который может содержать как абстрактные методы (без реализации), так и конкретные методы (с реализацией). Он может иметь поля, конструкторы, модификаторы доступа. В Java/Kotlin абстрактный класс может наследоваться только одним классом (одиночное наследование).

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

Ключевые технические различия

// Пример абстрактного класса
abstract class Animal(val name: String) {
    // Поле с состоянием
    protected var age: Int = 0
    
    // Конструктор с параметром
    // Абстрактный метод
    abstract fun makeSound()
    
    // Конкретный метод с реализацией
    fun eat() {
        println("$name is eating")
    }
}

// Пример интерфейса
interface Flyable {
    // Абстрактный метод
    fun fly()
    
    // Метод с реализацией (Kotlin)
    fun maxAltitude(): Int {
        return 1000
    }
    
    // Не может содержать состояние (поля) напрямую
    // val wingspan: Int = 2 // Ошибка!
}

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

  1. Когда нужна общая базовая реализация для группы родственных классов
  2. Когда необходимо сохранить состояние (поля класса) в базовой реализации
  3. Когда требуется контроль доступа к методам (protected, private)
  4. Для шаблонного метода (Template Method) — когда общий алгоритм определен в базовом классе, а конкретные шаги реализуются в наследниках
abstract class DataProcessor {
    // Шаблонный метод
    fun processData() {
        validateData()
        transformData()
        saveData()
        logResult()
    }
    
    protected abstract fun transformData()
    protected abstract fun saveData()
    
    private fun validateData() { /* общая реализация */ }
    private fun logResult() { /* общая реализация */ }
}

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

  1. Для определения контракта/способности (capability), которую могут реализовать несвязанные классы
  2. Когда нужно множественное "наследование" поведения (класс может реализовать несколько интерфейсов)
  3. Для соблюдения принципа Interface Segregation (разделения интерфейсов)
  4. В архитектуре на основе компонентов для слабой связанности
  5. Для реализации полиморфизма без создания иерархии наследования
// Несколько интерфейсов для одного класса
class Bird : Animal("Sparrow"), Flyable, Singable {
    override fun makeSound() { println("Chirp!") }
    override fun fly() { println("Flying high") }
    override fun sing() { println("Melody") }
}

interface ClickListener {
    fun onClick()
}

interface SwipeListener {
    fun onSwipe(direction: Direction)
}

Современные тенденции (Kotlin/Java 8+)

В современных языках границы между абстрактными классами и интерфейсами размываются:

  • Интерфейсы могут иметь реализацию методов (через default в Java или напрямую в Kotlin)
  • Но интерфейсы все еще не могут хранить состояние (не могут иметь fields/properties с backing fields)

Практические рекомендации

Выбирайте интерфейс, когда:

  • Определяете контракт для внешнего API
  • Нужна поддержка множественного наследования поведения
  • Создаете легковесные контракты для слабой связанности
  • Работаете с библиотеками и фреймворками (например, колбэки в Android)

Выбирайте абстрактный класс, когда:

  • Есть четкая иерархия "is-a" (является)
  • Необходимо разделить общую логику между родственными классами
  • Требуется контроль над конструкторами и инициализацией
  • Нужно инкапсулировать общее состояние

Пример из Android разработки

// Абстрактный класс для RecyclerView.Adapter
abstract class BaseAdapter<T> : RecyclerView.Adapter<BaseViewHolder>() {
    protected val items = mutableListOf<T>()
    
    fun updateData(newItems: List<T>) {
        items.clear()
        items.addAll(newItems)
        notifyDataSetChanged()
    }
    
    override fun getItemCount(): Int = items.size
    
    abstract override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder
}

// Интерфейсы для колбэков
interface ItemClickListener {
    fun onItemClick(position: Int)
}

interface ItemLongClickListener {
    fun onItemLongClick(position: Int): Boolean
}

Золотое правило: Предпочитайте интерфейсы абстрактным классам, если нет явной необходимости в последних. Интерфейсы обеспечивают большую гибкость и лучше соответствуют принципам SOLID, особенно принципам Interface Segregation и Dependency Inversion.