50
protected
Чтобы понять смысл спецификатора доступа protected, необходимо немного забежать вперед. Сразу скажу, что понимание этого раздела не обязательно до знакомства с наследованием (глава 7). И все же для получения цельного представления здесь приводится описание protected и примеры его использования.
Ключевое слово protected тесно связано с понятиемнаследования, при котором к уже существующему классу (называемомубазовым классом)добавляются новые члены, причем исходная реализация остается неизменной. Также можно изменять поведение уже существующих членов класса. Для создания нового класса на базе существующего используется ключевое слово extends:
class Foo extends Bar {
Остальная часть реализации выглядит как обычно.
Если при создании нового пакета используется наследование от класса, находящегося в другом пакете, новый класс получает доступ только к открытым (public) членам из исходного пакета. (Конечно, при наследовании в пределах одного пакета можно получить доступ ко всем членам с пакетным уровнем доступа.) Иногда создателю базового класса необходимо предоставить доступ к конкретному методу производным классам, но закрыть его от всех остальных. Именно для этой задачи используется ключевое слово protected. Спецификатор protected также предоставляет доступ в пределах пакета — то есть члены с этим спецификатором доступны для других классов из того же пакета.
Интерфейс и реализация
Контроль над доступом часто называютсокрытием реализации.Помещение данных и методов в классы в комбинации с сокрытием реализации часто называютинкапсуляцией.В результате появляется тип данных, обладающий характеристиками и поведением.
Доступ к типам данных ограничивается по двум причинам. Первая причина — чтобы программисту-клиенту знать, что он может использовать, а что не может. Вы вольны встроить в структуру реализации свои внутренние механизмы, не опасаясь того, что программисты-клиенты по случайности используют их в качестве части интерфейса.
Это подводит нас непосредственно ко второй причине — разделению интерфейса и реализации. Если в программе использована определенная структура, но программисты-клиенты не могут получить доступ к ее членам, кроме отправки сообщений риЬНс-интерфейсу, вы можете изменять все, что не объявлено как public (члены с доступом в пределах пакета, protected и private), не нарушая работоспособности изменений клиентского кода.
Для большей ясности при написании классов можно использовать такой стиль: сначала записываются открытые члены (public), затем следуют защищенные члены (protected), потом — с доступом в пределах пакета и наконец закрытые члены (private). Преимущество такой схемы состоит в том, что при чтении исходного текста пользователь сначала видит то, что ему важно (открытые члены, доступ к которым можно получить отовсюду), а затем останавливается при переходе к закрытым членам, являющимся частью внутренней реализации:
//. access/OrganizedByAccess.java public class OrganizedByAccess {
public void publO {
/*
*/ }
public void pub2() {
/*
*/ }
public void pub3() {
/*
. */ }
private void privlO
{ /* ■
*/
private void priv20
{ /* •
*/
private void priv30
{ /*
*/
private int i;
// ..
} III -
Такой подход лишь частично упрощает чтение кода, поскольку интерфейс и реализация все еще совмещены. Иначе говоря, вы все еще видите исходный код — реализацию — так, как он записан прямо в классе. Вдобавок документация в комментариях, создаваемая с помощью javadoc, снижает необходимость в чтении исходного текста программистом-клиентом.
Доступ к классам
В Java с помощью спецификаторов доступа можно также указать, какие из классоввнутрибиблиотеки будут доступны для ее пользователей. Если вы хотите, чтобы класс был открыт программисту-клиенту, то добавляете ключевое слово public для класса в целом. При этом вы управляете даже самой возможностью создания объектов данного класса программистом-клиентом.
Для управления доступом к классу, спецификатор доступа записывается перед ключевым словом class:
public class Widget {
Если ваша библиотека называется, например, access, то любой программист-клиент сумеет обратиться извне к классу Widget:
import access Widget: или
import access *;
Впрочем, при этом действуют некоторые ограничения:
• В каждом компилируемом модуле может существовать только один открытый (public) класс. Идея в том, что каждый компилируемый модуль содержит определенный открытый интерфейс и реализуется этим открытым классом. В модуле может содержаться произвольное количество вспомогательных классов с доступом в пределах пакета. Если в компилируемом модуле определяется более одного открытого класса, компилятор выдаст сообщение об ошибке.
• Имя открытого класса должно в точности совпадать с именем файла, в котором содержится компилируемый модуль, включая регистр символов. Поэтому для класса Widget имя файла должно быть Widget.java, но никак не widget.java или WIDGET.java. В противном случае вы снова получите сообщение об ошибке.
• Компилируемый модуль может вообще не содержать открытых классов (хотя это и не типично). В этом случае файлу можно присвоить любое имя по вашему усмотрению. С другой стороны, выбор произвольного имени создаст трудности у тех людей, которые будут читать и сопровождать ваш код.
Допустим, в пакете access имеется класс, который всего лишь выполняет некоторые служебные операции для класса Widget или для любого другого ри- ЬИс-класса пакета. Конечно, вам не хочется возиться с созданием лишней документации для клиента; возможно, когда-нибудь вы просто измените структуру пакета, уберете этот вспомогательный класс и добавите новую реализацию. Но для этого нужно точно знать, что ни один программист-клиент не зависит от конкретной реализации библиотеки. Для этого вы просто опускаете ключевое слово public в определении класса; ведь в таком случае он ограничивается пакетным доступом, то есть может использоваться только в пределах своего пакета.
При создании класса с доступом в пределах пакета его поля все равно рекомендуется помечать как private (всегда нужно по максимуму перекрывать доступ к полям класса), но методам стоит давать тот же уровень доступа, что имеет и сам класс (в пределах пакета). Класс с пакетным доступом обычно используется только в своем пакете, и делать методы такого класса открытыми (public) стоит только при крайней необходимости — а о таких случаях вам сообщит компилятор.