Александр Головня

+17
с 2021

Инженер-программист, консультант и лектор. Проектирую архитектуру и разрабатываю высоконагруженные системы в здравоохранении, телекоме и финтехе.

10 подписчиков
41 подписка

В случае абстрактных классов также просматриваются два основных сценария. Первый — это необходимость иерархии классов с общим состоянием и поведением. В этой статье мы рассматривали именно этот случай на примере абстрактного класса для описания свойств bounce-сообщений. Так как интерфейсы не могут иметь состояния, то использование абстрактных классов здесь единственно возможное решение.

Второй сценарий — это предоставление базовой реализации для связанных классов. Да, интерфейсы поддерживает методы по-умолчанию и локальные переменные, но это подходит для решения только самых тривиальных задач. К примеру, мы не можем использовать внедрение зависимостей (DI) в интерфейсе, а это является ключевой частью любого промышленного приложения. Поэтому лучшей практикой при предоставлении базового функционала является либо использование абстрактных классов, либо использование композиции.

1

Постараюсь ответить кратко, потому что про сценарии использования можно написать целую отдельную статью :)

В этой статье мы говорили про пример с определением контракта для класса и что с этим лучше всего справляется интерфейс. Это один из однозначных сценариев когда нужно использовать именно интерфейс. Другими словами это еще могут называть API класса. Казалось бы абстрактный класс тоже отлично справиться с заданием контракта и это действительно так, но из-за того что Java не поддерживает множественное наследование, мы всегда будем ограничены только одним базовым классом. А так как в реальной практике нам очень часто нужно использовать несколько разных контрактов, то абстрактные классы будут неподходящим инструментом для этого случая и лучшей практикой считается использование интерфейсов.

Другой пример, когда однозначно нужно использовать интерфейс — это реализация так называемых признаков и маркеров. Например, если вам нужно сравнивать объекты, вы можете реализовать интерфейс "Comparable" чтобы быть уверенным, что класс поддерживает сравнение. В свою очередь интерфейс "Cloneable", который является маркером, будет просто означать что ваш тип поддерживает клонирование.

1

Хороший вопрос! С одной стороны да, если задуматься по логике, то в чем смысл sealed класса или интерфейса если все можно легко обойти? Но если посмотреть в стандарт JEP 409, то можно заметить, что создатели языка не преследовали цели предоставить новый модификатор доступа:
1. «It is not a goal to provide new forms of access control such as "friends"»
2. «It is not a goal to change final in any way.»

Sealed класс/интерфейс дает более гибкий контроль над наследованием. Он может быть наследован только ограниченным заранее определенным набором других классов. Это позволяет нам контролировать наследование в иерархии классов, но при этом давая возможность подклассам самим решать вопрос дальнейших ограничений. Главная цель здесь — это предотвратить непреднамеренное или некорректное наследование, что делает более ясной структуру нашей программы. Это также может помочь компилятору Java делать некоторые оптимизации, поскольку он может быть уверен в иерархии классов.

Если нам нужно гарантировать, что поведение класса не будет изменено через наследование, то нужно использовать модификатор final для класса, что полностью заблокирует дальнейшее наследование. В этой статье, классы Circle и Rectangle, наследующие от sealed класса Shape, как раз и объявлены финальными.

2