Java позволяет реализовывать полиморфизм двумя ключевыми механизмами: абстрактными классами и интерфейсами. Несмотря на очень похожие концепции, они имеют важные различия, которые важно понимать для разработки эффективных приложений и успешного прохождения технических собеседований. В этой статье мы рассмотрим основные сценарии использования и теор…
Спасибо за полезную статью!
Хотелось бы более подробно узнать про сценарии использования
Может есть какие-то четкие примеры, когда нужно использовать абстрактные классы, а когда - только интерфейсы?
Постараюсь ответить кратко, потому что про сценарии использования можно написать целую отдельную статью :)
В этой статье мы говорили про пример с определением контракта для класса и что с этим лучше всего справляется интерфейс. Это один из однозначных сценариев когда нужно использовать именно интерфейс. Другими словами это еще могут называть API класса. Казалось бы абстрактный класс тоже отлично справиться с заданием контракта и это действительно так, но из-за того что Java не поддерживает множественное наследование, мы всегда будем ограничены только одним базовым классом. А так как в реальной практике нам очень часто нужно использовать несколько разных контрактов, то абстрактные классы будут неподходящим инструментом для этого случая и лучшей практикой считается использование интерфейсов.
Другой пример, когда однозначно нужно использовать интерфейс — это реализация так называемых признаков и маркеров. Например, если вам нужно сравнивать объекты, вы можете реализовать интерфейс "Comparable" чтобы быть уверенным, что класс поддерживает сравнение. В свою очередь интерфейс "Cloneable", который является маркером, будет просто означать что ваш тип поддерживает клонирование.
В случае абстрактных классов также просматриваются два основных сценария. Первый — это необходимость иерархии классов с общим состоянием и поведением. В этой статье мы рассматривали именно этот случай на примере абстрактного класса для описания свойств bounce-сообщений. Так как интерфейсы не могут иметь состояния, то использование абстрактных классов здесь единственно возможное решение.
Второй сценарий — это предоставление базовой реализации для связанных классов. Да, интерфейсы поддерживает методы по-умолчанию и локальные переменные, но это подходит для решения только самых тривиальных задач. К примеру, мы не можем использовать внедрение зависимостей (DI) в интерфейсе, а это является ключевой частью любого промышленного приложения. Поэтому лучшей практикой при предоставлении базового функционала является либо использование абстрактных классов, либо использование композиции.