ПрограммированиеKotlin разработчик

Чем отличаются обычные классы, абстрактные классы и интерфейсы в Kotlin, когда и какой инструмент применять?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

История вопроса:

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

Проблема:

Правильный выбор между классом, абстрактным классом и интерфейсом определяет архитектуру приложения, гранулярность кода и его расширяемость. Неправильное наследование приводит к сложности тестирования и будущих изменений.

Решение:

В Kotlin:

  • Обычный класс: задает структуру и поведение. Можно наследовать, если объявить open.
  • Абстрактный класс: не может быть создан напрямую. Может содержать реализацию и/или абстрактные (без тела) методы.
  • Интерфейс: неявно открытый, реализует контракты, допускает реализацию методов, но не хранение состояния (только свойства без backing field).

Пример кода:

interface Drawable { fun draw() } abstract class Shape(var color: String) : Drawable { abstract fun calcArea(): Double override fun draw() = println("Shape drawn") } class Circle(color: String, val radius: Double) : Shape(color) { override fun calcArea() = Math.PI * radius * radius }

Ключевые особенности:

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

Вопросы с подвохом.

Могут ли интерфейсы содержать свойства с backing field?

Нет, они могут определять только сигнатуру свойства, но не хранить данные — свойства без backing field.

Можно ли унаследоваться от нескольких классов?

Нет, Kotlin поддерживает только одиночное наследование классов, но множественную реализацию интерфейсов.

Можно ли объявить конструктор у интерфейса?

Нет, интерфейс не поддерживает конструкторы, потому что он не хранит состояние — только контракт поведения.

Типовые ошибки и анти-паттерны

  • Использование abstract class вместо interface без необходимости
  • Хранение состояния в interface (невозможно, но ошибаются при проектировании)
  • Проектирование иерархий классов, затрудняющих расширение

Пример из жизни

Негативный кейс

В приложении все общие функции вынесли в абстрактный класс, даже если нет внутренней логики или состояния, а потребность — просто общий контракт.

Плюсы:

  • Одна точка изменения общей логики

Минусы:

  • Проблемы с множественным наследованием, трудная интеграция с другими структурами

Позитивный кейс

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

Плюсы:

  • Гибкое расширение, модульность, чистые контракты

Минусы:

  • Требует проектирования на ранней стадии