История вопроса:
Kotlin объединил лучшие стороны Java и функционального наследия JVM. Обычные классы объявляются как стандартные структуры, абстрактные классы позволяют создавать недоопределенные шаблоны с реализацией по умолчанию, а интерфейсы поддерживают множественное наследование поведения без состояния.
Проблема:
Правильный выбор между классом, абстрактным классом и интерфейсом определяет архитектуру приложения, гранулярность кода и его расширяемость. Неправильное наследование приводит к сложности тестирования и будущих изменений.
Решение:
В Kotlin:
Пример кода:
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 поддерживает только одиночное наследование классов, но множественную реализацию интерфейсов.
Можно ли объявить конструктор у интерфейса?
Нет, интерфейс не поддерживает конструкторы, потому что он не хранит состояние — только контракт поведения.
В приложении все общие функции вынесли в абстрактный класс, даже если нет внутренней логики или состояния, а потребность — просто общий контракт.
Плюсы:
Минусы:
Вынесли только нужные контракты в интерфейсы, абстрактные классы ограничили общими свойствами и методами, требующими реализации.
Плюсы:
Минусы: