В библиотеке класс объявлен как public или open: какое различие это создаёт для наследования из другого модуля?
Класс с модификатором public доступен внешнему модулю, но наследоваться от него там нельзя. Класс с модификатором open разрешает внешнее наследование; при этом переопределять извне можно только его члены, также объявленные как open.
Разделение public и open задаёт явную границу расширения библиотечного API. Оно позволяет автору библиотеки отделить типы, которыми можно пользоваться, от типов, поведение которых разрешено изменять через наследование.
Без такого различия публикация класса автоматически создавала бы более широкий контракт совместимости: внешний код мог бы зависеть от возможности наследования и переопределения методов.
Представим фреймворк, который публикует базовый класс. Если внешний модуль сможет свободно наследоваться от любого публичного класса, изменение его реализации или добавление ограничений может нарушить клиентский код.
Обратная ошибка тоже возможна: если класс предназначен для создания адаптеров и расширений, объявление только как public не позволит клиенту реализовать такое расширение. Поэтому важно отдельно определить доступность типа и возможность его наследования.
public-класс можно использовать во внешнем модуле: создавать его экземпляры, передавать значения и обращаться к доступным членам. Однако внешний модуль не может объявить его наследника.
open-класс разрешает наследование за пределами модуля. Для переопределения метода или свойства член тоже должен быть open. Одной доступности public для внешнего переопределения недостаточно.
Внешний модуль сможет унаследоваться от OpenBase и переопределить run, но не сможет унаследоваться от PublicBase. Модификатор open применим только к классам и их членам, допускающим переопределение; для полного запрета наследования используется final.
Выбор open — это расширение публичного контракта и дополнительное обязательство по совместимости. Если наследование не является частью дизайна API, безопаснее оставить класс public, чтобы сохранить контроль над его иерархией.
Команда разрабатывает сетевой фреймворк и публикует класс клиента. Для обычных приложений достаточно создавать клиент и вызывать его методы, поэтому вариант public лучше ограничивает поверхность API и снижает риск внешних зависимостей от деталей наследования.
Если же фреймворк специально предусматривает пользовательские реализации транспорта, возможны два варианта. Можно предоставить протокол: это обычно гибче и слабее связывает клиента с иерархией классов. Либо объявить базовый класс open и пометить точки расширения как open; такой подход удобен, когда нужна готовая реализация и контролируемое переопределение.
Выбранное решение должно быть явным: для расширяемого базового класса используется open, для просто доступного типа — public. Это предотвращает ошибочное ожидание, что любой публичный класс можно унаследовать во внешнем модуле.
Нет. Класс должен быть open, а конкретный метод — тоже open. public делает метод доступным для вызова, но не разрешает его переопределение во внешнем модуле.
Да. Ограничение относится к наследованию из другого модуля. Внутри модуля, где класс объявлен, его public-члены и сам класс могут участвовать в обычном наследовании, если дополнительно не установлен final.
open превращает наследование и переопределение в поддерживаемую часть контракта. Клиенты могут начать зависеть от конкретных точек расширения, а изменение этих точек станет сложнее без нарушения совместимости. Если расширение не предусмотрено дизайном, public лучше сохраняет свободу развития реализации.