В Kotlin абстрактные классы (abstract class) и интерфейсы (interface) используются для объявления абстрактной логики, но между ними есть существенные различия:
abstract class Animal(val name: String) { abstract fun makeSound() fun info() = println("I am $name") } interface Movable { fun move() } class Cat(name: String) : Animal(name), Movable { override fun makeSound() = println("Meow") override fun move() = println("Cat moves") }
Может ли интерфейс в Kotlin содержать состояния (backing field)?
Большинство отвечает, что можно объявить свойства в интерфейсе, но они все равно не имеют backing field — только геттеры/сеттеры без хранения значения. Даже если написать get() = ... — значение будет вычисляться каждый раз.
Пример:
interface MyInterface { val value: Int get() = 42 // нет хранения значения, вычисление на лету }
История
На крупном проекте старший разработчик пытался хранить состояние в интерфейсе (например, счетчик), ожидая, что свойство var count: Int обзаведется backing field. В результате у всех реализующих интерфейс классов приходилось реализовывать хранение значения по-разному, что вылилось в нестабильную логику (данные терялись, если забывали переопределить setter).
История
Один из разработчиков использовал абстрактный класс там, где требовалась множественная реализация различных стратегий поведения (паттерн стратегия). В результате возникла проблема: лишь один базовый класс на иерархию, но для разных аспектов поведения требовалась композиция. Пришлось менять архитектуру на интерфейсы.
История
Команда пыталась наследовать сразу два абстрактных класса (один с логикой, другой с общими утилитами), что невозможно в Kotlin. Проблема выявилась уже на стадии расширения приложения. Это вылилось в значительный технический долг, поскольку классы пришлось срочно реорганизовывать.