Главная » Java » Использование имен Java

0

Имена различных компонентов программы – типов, переменных, полей, методов и Т.д. – задаются посредством идентификаторов (identifiers). Когда конкретное имя употребляется в исходном коде, компилятор обязан определить, на какой компонент программы оно указывает, и проверить правильность его использования, чтобы суметь сформировать исполняемый код. В правилах использования имен реализован определенный компромисс между удобством и эффективностью программирования с одной стороны и сложностью допустимых конструкций с другой. Если говорить об одном из двух крайних подходов, язык может накладывать жесткое требование, касающееся уникальности всех имен, которые употребляются в тексте программы. Подобное решение значительно упрощает задачу компилятора, но существенно усложняет участь программиста. Действуя в соответствии со вторым подходом, автор языка вправе допустить возможность интерпретации имен в зависимости от контекста, в котором они используются, – программист окажется в выигрыше, приобретя шанс повторного использования имен (как, например, в случае применения переменной i в качестве счетчика итераций цикла for), но компилятору придется "поразмыслить" над значением каждого имени (что, собственно, и делается внимательным читателем программного кода и добротным компилятором).

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

Существует шесть различных пространств имен:

·         пакетов;

·         типов;

·         полей;

·         методов;

·         Локальных переменных и параметров;

·         меток.

Контекст имени, используемого в программе, помогает определить, к какой категории компонентов относится имя. Например, глядя на выражение х.f = 3, мы понимаем, что идентификатор f должен обозначать поле – f не может быть именем пакета, типа, метода или метки, поскольку используется в левой части оператора присваивания, и не принадлежит к категории локальных переменных, так как применяется совместно с квалификатором х. Что касается х, то в этой роли может выступать наименование типа или объекта, а также поля или Локальной переменной, если таковые относятся к ссылочным типам; более точныe сведения о природе х можно получить с помощью поиска соответствующего объявления в ближайших контекстах.

Механизм разделения пространства имен обеспечивает гибкие возможности

написания программ (особенно в тех случаях, когда сочетаются фрагменты кода из разных источников), но при неразумном использовании он способен доставлять и серьезные неприятности. Приведем в качестве примера один просто-таки душераздирающий, но формально вполне правильный фрагмент кода:

package Reuse;

class Reuse {

 

Reuse Reuse(Reuse Reuse) {

Reuse:

for (;;) {

 

if (Reuse.Reuse(Reuse) == Reuse)

break Reuse;

}

return Reuse;

}

}

Каждое объявление имени определяет контекст, в котором имя может использоваться. Точные правила различаются в зависимости от того, к какой категории относится компонент, обозначенный этим именем, – к типам, членам классов, локальным переменным и Т.д. Например, контекстом параметра метода служит блок тела этого метода; контекст локальной переменной определяется границами блока, в котором она определена; контекст переменной цикла, объявленной в секции инициализации заголовка цикла for, распространяется на блок тела цикла.

Имя нельзя использовать за пределами его контекста – например, один метод класса не способен обращаться к параметрам другого метода. Контексты, однако, могут быть вложенными, и код внутреннего контекста обладает правами доступа ко всем именам внешнего контекста. Например, разрешено обращаться из тела цикла for к локальным переменным, объявленным в пределах того же метода, где находится и цикл for.

При обращении к имени, обозначающем поле или локальную переменную,

смысл имени определяется посредством поиска соответствующего объявления в пределах текущего и внешних контекстов и в различных пространствах имен. Процесс поиска протекает следующим образом:

1)    проверяются локальные переменные, объявленные в текущем блоке или цикле for, либо параметры предложения catch конструкции try, затем локальные переменные внешнего блока; процедура выполняется рекурсивно до достижения границ тела метода или "самого" внешнего блока (как в случае блока инициализации);

2)    если код находится в теле метода или конструктора, проверяются параметры метода или конструктора;

3)    проверяются члены (поля и методы) класса или интерфейса, включая и доступные унаследованные члены;

4)    если код принадлежит вложенному типу, проверяется внешний блок или класс, причем если вложенный тип относится к разновидности статических, проверяются только статические члены внешнего блока или класса; процесс повторяется рекурсивно для всех блоков или классов, внешних по отношению к текущему.

Процесс доступа к членам класса регламентируется особыми правилами, о которых мы расскажем ниже, в разделе 6.9 на странице 190.

Порядок поиска определяет, какое из объявлений будет найдено; подразумевается, что имена, объявленные во внешних контекстах, могут быть скрыты именами, принадлежащими внутренним контекстам. Так, например, имена членов класса Способны перекрываться именами локальных переменных, члены внешнего экземпляра – членами вложенного класса, а унаследованные члены класса – членами, объявленными в текущем классе (подобные примеры вы уже видели).

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

Стремясь снизить вероятность возникновения недоразумений, компилятор не позволяет перекрывать имена во вложенных контекстах внутри одного блока кода. Это значит, что локальная переменная в теле метода не может обладать тем же именем, что и параметр метода; переменную цикла for не разрешается обозначать именем, которое принадлежит локальной переменной или параметру; во вложенном блоке нельзя повторно использовать имена, объявленные во внешнем блоке:

{

int uber = о; {

int uber = 2; // НЕВЕРНО: переменная уже объявлена // ‘"

      }

}

Впрочем, ничто не запрещает создавать в пределах блока различные (не вложенные) циклы for или применять не вложенные блоки с объявлениями одноименных переменных.

Если имя принадлежит пространству имен типов, в процессе поиска должны быть просмотрены различные контексты типов. Контексты типов определяются на уровне пакетов. В подобной ситуации последовательно проверяются следующие контексты:

1)    объявления текущего типа и унаследованных типов;

2)    объявления типов, вложенных в текущий;

3)    явно указанные импортированные типы;

4)другие типы, объявленные в том же пакете;

5) неявно заданные импортированные типы.

Хотя перекрытие имен типов и допустимо, имя требуемого типа всегда может быть получено посредством задания полного квалификатора, включающего название пакета (например, jаvа.lang.String). Вопросам применения пакетов и инструкций импорта посвящена глава 13.

Источник: Арнолд, Кен, Гослинг, Джеймс, Холмс, Дэвид. Язык программирования Java. 3-е изд .. : Пер. с англ. – М. : Издательский дом «Вильяме», 2001. – 624 с. : ил. – Парал. тит. англ.

По теме:

  • Комментарии