Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Джош Блох

.pdf
Скачиваний:
57
Добавлен:
08.03.2016
Размер:
27.13 Mб
Скачать

Глава 7 Методы

Заметим, что в этом doc-комментарии используются метасимволы и теги языка H TM L. Утилита Javadoc преобразует doc-комментарии в код H T M L, и любые содержавшиеся в doc-комментариях элементы H T M L оказываются в полученном H T M L -документе. Иногда про­ граммисты заходят настолько далеко, что встраивают в свои doc-ком- ментарии таблицы H TM L, хотя это не является общепринятым.

Также отметим использование Javadoc тега {@code} вокруг фраг­ мента кода в выражении @ throw. Это необходимо для выполнения двух задач: приводит к тому, что фрагмент кода отображается шрифтом кода (code font) и он скрывает обработку H T M L разметки вложенных Javadoc тегов в фрагменте кода. Последнее свойство — это то, что нам дает возможность использования знака меньше чем ( < ) в фрагмен­ те кода, даже если это метасимвол H TM L. До версии 1.5 фрагменты кода включались в doc-комментарии, используя H T M L . Больше нет необходимости использовать H T M L теги <code> или < tt> в doc-ом- ментариях: Javadoc тег {@code} использовать предпочтительнее, так как он избавляет нас от необходимости избегать использование ме­ тасимволов H TM L. Для добавления многострочного примера кода в doc-комментарий используйте тег Javadoc {@code} внутри H T M L тега <рге>. Другими словами, пример многострочного кода должен начинаться с <pre> { @code и заканчиваться символами } < /р ге> .

Наконец, отметим появление в doc-комментарии слова «this». По соглашению, слово «this» всегда ссылается на тот объект, кото­ рому принадлежит вызываемый метод, соответствующий данному doc-комментарию.

Не забудьте, что вам нужно предпринять специальные действия, чтобы сгенерировать документацию, содержащую метасимволы H TM L, такие как знаки < , > , &. Лучший способ вставить эти сим­ волы в документацию — это заключить их в тег {@literal}, который скрывает обработку H T M L разметки во вложенном Javadoc теге. Это как с тегом {@code}, за исключением того, что он не переводит текст в шрифт кода. Например, фрагмент Javadoc:

* The triangle inequality is {(©literal |x + y| < |x| + [у [}.

280

С тать я 44

Генерирует следующую документацию: «Неравенство треуголь­ ника |х + у| < |х| + |у|.»Тег {@literal} мог быть помещен только во­ круг знака меньше чем, вместо того чтобы все неравенство, причем документация получилась бы такой же, но тогда doc-комментарий было бы сложнее читать в исходном коде. Этот пример иллюстри­ рует общий принцип, что doc-комментарии должны быть читаемы не только в сгенерированной документации, но и в исходном коде. Если и того и другого достичь не удается, то предпочтительнее, ко­ нечно, читаемость сгенерированной документации.

Первым предложением любого doc-комментария является общее описание (summary description) того элемента, к которому этот ком­ ментарий относится. Общее описание должно позиционироваться как описывающее функции соответствующей сущности. Во избежа­ ние путаницы никакие два члена или конструктора в одном классе или интерфейсе не должны иметь одинакового общего описания. Особое внимание обращайте на перезагруженные методы, описание которых часто хочется начать с одного и того же предложения.

Внимательно следите за тем, чтобы в первом предложении doc-комментария не было точки. В противном случае общее описа­ ние будет завершено прежде времени. Например, комментарий к до­ кументации, который начинается с фразы «А college degree, such as B.S., M.S., or Ph.D.», приведет к появлению в общем описании фразы «А college degree, such as В .» Во избежание подобных проблем лучше не использовать в общем описании сокращений и десятичных дробей. Для получения символа точки нужно заменить его числовым представлением (numeric encoding) «. ». Хотя это работает, ис­ ходный текст программы приобретает не слишком красивый вид:

j * *

* A college degree, such as B.S., {@literal M.S.} or Ph.D.

* College is a fountain of knowledge where many go to drink.*/ public class Degree { }

Тезис, что первым предложением в doc-комментарии является общее описание, отчасти вводит в заблуждение. По соглашению, оно

281

Глава 7 Методы

редко бывает законченным предложением. Общее описание методов и конструкторов должно представлять собой глагольную конструк­ цию (verb phrase — термин грамматики английского языка), которая описывает операцию, осуществляемую этим методом. Например:

ArrayList(int initialCapacity)

создает пустой список

 

с заданной начальной емкостью.

 

Collection. size() — возвращает

количество элементов

 

в указанной коллекции.

 

Общее описание классов, интерфейсов и полей должно быть именной конструкцией, описывающей сущность, которую представ­ ляет экземпляр этого класса, интерфейса или само поле. Например:

ТimerTask — задача, которая может быть спланирована клас­ сом Timer для однократного или повторяющегося исполнения.

Math.PI — значение типа double, наиболее близкое к числу «пи» (отношение длины окружности к ее диаметру).

Три возможности, добавленные к языку в версии 1.5, требуют особого отношения в doc-комментариях: средства обобщенного про­ граммирования, перечислимые типы и аннотации. Когда в ы пишете документацию по обобщенным типам или методам, обязательно документируйте все параметры типа:

/ * *

 

 

 

 

* An object

that

maps

keys to values. A map cannot

contain

* duplicate

keys;

each

key can map to at most one

value.

■k

* (Remainder omitted)

*

*§param <K> the type of keys maintained by this map

*@param <V> the type of mapped values

*/

public interface Map<K, V> {

... // Remainder omitted

}

282

С тать я 44

П р и документировании перечислимого типа убедитесь, что документированы константы, тип и любой открытый метод. О б­ ратите внимание, что вы можете поместить весь doc-комментарий в одну строку, если он краткий:

/ * *

* An instrument section of a symphony orchestra.

*/

public enum OrchestraSection {

/** Woodwinds, such as flute, clarinet, and oboe. */

WOODWIND,

/** Brass instruments, such as french horn and trumpet. */

BRASS,

/** Percussion instruments, such as timpani and cymbals */

PERCUSSION,

/** Stringed instruments, such as violin and cello. */

STRING;

}

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

j * *

 

 

* Indicates that the annotated method

is

a test method that

* must throw the designated exception

to

succeed.

*/

(©Retention(RetentionPolicy. RUNTIME) <©Target(ElementType.METHOD)

public (©interface ExceptionTest {

J к к

* The exception that the annotated test method must throw

*in order to pass. (The test is permitted to throw any

*subtype of the type described by this class object.)

283

Глава 7 Методы

*/

Class<? extends Throwable> value();

}

В версии 1.5 doc-комментарии пакетного уровня следует по­ мещать в файл, называемый package-info.java, вместо package.html. В дополнение к doc-комментарию пакетного уровня package-info.java должен содержать декларацию пакета и может содержать аннотацию пакета для данной декларации.

Двумя аспектами экспортируемого A PI класса, которые зача­ стую игнорируются, являются потоковая безопасность и возмож­ ность сериализации. Является или нет класс безопасным с точки зрения потока, должно быть документировано на уровне потока, как описано в статье 70. Если класс может быть сериализуем, нужно до­ кументировать его сериализованную форму, как описано в статье 75.

Утилита Javadoc получила возможность «наследовать» коммен­ тарии к методам. Если метод не имеет doc-комментария, Javadoc находит среди приемлемых наиболее близкий doc-комментарий, отдавая при этом предпочтение интерфейсам, а не суперклассам. Подробности алгоритма поиска комментариев можно найти в The Javadoc Reference Guide [Javadoc-ref]. Вы можете наследовать части doc-комментариев их супертипов, используя тег {@inheritDoc}.

Это означает, что классы теперь могут заимствовать doc-ком­ ментарии из реализуемых ими интерфейсов вместо того, чтобы копи­ ровать комментарии у себя. Такая возможность способна сократить или вовсе снять бремя поддержки многочисленных наборов почти идентичных doc-комментариев, но у нее есть несколько ограничений. Их детали не вошли в книгу.

Простейший способ уменьшить вероятность появления ошибок в комментариях к документации — это пропустить H T M L -файлы, сгенерированные утилитой Javadoc, через программу проверки кода H T M L (H TM L validity checker). При этом будет обнаружено мно­ жество случаев неправильного использования тегов и метасимволов H TM L, которые нужно исправить. Некоторые из программ провер-

284

С тать я 44

ки кода НТМЬдоступны в Интернете для загрузки, или вы можете проверить H T M L код онлайн [W 3C-validator].

Следует добавить еще одно предостережение, связанное с ком­ ментариями к документации. Комментарии должны сопровождать все элементы внешнего API, но этого не всегда достаточно. Для сложных API, состоящих из множества взаимосвязанных классов, комментарии к документации часто требуется дополнять внешним документом, описывающим общую архитектуру данного A PI. Если такой документ существует, то комментарии к документации в соот­ ветствующем классе или пакете должны на него ссылаться.

Соглашения, описанные в данной статье, составляют основы. Более детальное руководство, как писать doc-комментарии, содер­ жится в How То Write Doc Comments [Javadoc-guide], выпущенном компанией Sun. Имеются ID E плагины, которые проверяют следова­ ние многим из этих правил [BurnOl].

Подведем итоги. Комментарии к документации — самый луч­ ший, самый эффективный способ документирования API. Написа­ ние комментариев нужно считать обязательным для всех элементов внешнего API. Выберите стиль, который не противоречит стан­ дартным соглашениям. Помните, что в комментариях к документа­ ции можно использовать любой код H TM L, причем метасимволы H T M L необходимо маскировать escape-последовательностями.

285

Г л а в а

г

1

Общие вопросы

программирования

Дранная глава посвящена обсуждению основных элементов языка Java. В ней рассматриваются интерпретация локальных переменных, использование библиотек и различных типов данных, а также две выходящие за рамки языка возможности: отражение (reflection)

и машинозависимые методы ( native method). Наконец, обсужда­ ются оптимизация и соглашения по именованию.

Сводите к минимуму область видимости локальных переменных

Эта статья по своей сути схожа со статьей 13 «Сводите к мини­ муму доступность классов и членов». Сужая область видимости ло­ кальных переменных, вы повышаете удобство чтения и сопровожде­ ния вашего кода, сокращаете вероятность возникновения ошибок.

Старые языки программирования, такие как С, указывают, что локальные переменные должны декларироваться в начале блока. И программисты продолжают придерживаться этого порядка, хотя

286

С тать я 45

от него уже нужно отказываться. Напомним, что язык программи­ рования Java позволяет объявлять переменную в любом месте, где может стоять оператор.

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

К расширению области видимости локальной переменной приводит не только слишком раннее, но и слишком позднее ее декларирование. Область видимости локальной переменной начинается в том месте, где она декларируется, и заканчивается с завершением блока, содержав­ шего эту декларацию. Если переменная декларирована за пределами блока, где она используется, то она остается видимой и после того, как программа выйдет из этого блока. Если переменная случайно была ис­ пользована до или после области, в которой она должна была приме­ няться, последствия могут быть катастрофическими.

Почти каждая декларация локальной переменной должна со­ держать инициализатор. Если у вас недостаточно информации для правильной инициализации переменной, вы должны отложить де­ кларацию до той поры, пока она не появится. Исключение из этого правила связано с использованием операторов try/catch. Если для инициализации переменной применяется метод, инициирующий по­ явление обрабатываемого исключения, то инициализация переменной должна осуществляться внутри блока t гу. Если переменная должна использоваться за пределами блока try, декларировать ее следует пе­ ред блоком try, там, где она еще не может быть «правильно инициа­ лизирована» (статья 53).

287

Глава 8 Общие вопросы программирования

Цикл предоставляет уникальную возможность для сужения об­ ласти видимости переменных. Цикл for позволяет объявлять пере­ менные цикла (loop variable), ограничивая их видимость ровно той областью, где они нужны. (Эта область состоит из собственно тела цикла, а также из предшествующих ему полей инициализации, про­ верки и обновления.) Следовательно, если после завершения цикла значения его переменных не нужны, предпочтение следует отдавать

циклам for, а не while.

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

//Не используются циклы for-each или средства обобщенного

//программирования до версии 1.51

for (Iterator i = с.iterator(); i.hasNext(); ) { doSomething((Element) i.nextO);

}

Для пояснения, почему данный цикл for предпочтительнее бо­ лее очевидного цикла while, рассмотрим следующий фрагмент кода, в котором содержатся два цикла while и одна ошибка:

Iterator<Element> i = с.iterator(); while (i.hasNext()) {

doSomething(i.next());

}

Iterator<Element> i2 = c2.iterator(); while (i.hasNextO) { // ОШИБКА!

doSomethingElse(i2.next());

}

Второй цикл содержит ошибку копирования фрагмента програм­ мы: инициализируется новая переменная цикла i 2, но используется старая i, которая, к сожалению, остается в поле видимости. Полу­ ченный код компилируется без замечаний и выполняется без ини­ циирования исключительных ситуаций, только вот делает не то, что нужно. Вместо того чтобы организовывать итерацию по с2, второй

288

С татья 45

цикл завершается немедленно, создавая ложное впечатление, что коллекция с2 пуста. И поскольку программа ничего об этой ошибке не сообщает, та может оставаться незамеченной долгое время.

Если бы аналогичная ошибка копирования была допущена при применении цикла for, полученный код не был бы даже скомпили­ рован. Для той области, где располагается второй цикл, переменная первого цикла уже была бы за пределами видимости:

for (Iterator<Element> i = с.iterator(); i.hasNext(); ) { doSomething(i.next());

}

// Ошибка компиляции - символ i не может быть идентифицирован for (Iterator<Element> i2 = с2. iterator(); i.hasNextO; ) {

doSomething(i2.next());

}

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

Идиома цикла for имеет еще одно преимущество перед идиомой цикла while, хотя и не столь существенное. Идиома цикла for короче на одну строку, что помогает при редактировании уместить содержа­ щий ее метод в окне фиксированного размера и повышает удобство чтения.

Приведем еще одну идиому цикла для просмотра списка, кото­ рая минимизирует область видимости локальных переменных:

for (int i = 0, n = expensiveComputation(); i < n; i++) { doSomething(i);

}

По поводу этой идиомы важно заметить, что у нее есть две пе­ ременные цикла: i и п, и обе имеют абсолютно правильную область

289

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]