Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Предпочитайте статические классы-члены нестатическим



 

 

Класс называется вложенным (nested), если он определен внутри другого класса.

Вложенный класс должен создаваться только для того, чтобы обслуживать окружаю­щий его класс. Если вложенный класс оказывается полезен в каком-либо ином кон­тексте, он должен стать классом верхнего уровня. Существуют четыре категории вложенных классов: статический класс-член (static member class), нестатический класс-член (nonstatic member class), анонимный класс (anonymoиs class) и локальный класс (local class). За исключением первого, остальные категории классов называются внутренними (inner class). В этой статье рассказывается о том, когда и какую катего­рию вложенного класса нужно использовать и почему.

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

В одном из распространенных вариантов статический класс-член используется как открытый вспомогательный класс, который пригоден для применения, только когда есть внешний класс. Например, рассмотрим перечисление, описывающее опе­рации, которые может выполнять калькулятор (статья 21). Класс Operation должен быть открытым статическим классом-членом класса Calculator. Клиенты класса Calculator могут ссылаться на операции, выполняемые калькулятором, используя такие имена, как Calculator.Ореration.PLUS или Calculator.Ореration.MINUS. Этот вариант приводится ниже.

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

 

 

87

 

экземпляром класса-контеинера (enclosing instance). Из метода в экземпляре неста­тического класса-члена можно вызывать методы содержащего его экземпляра, либо, используя специальную конструкцию this [JLS 15.8.4], можно получить ссылку на включающий экземпляр. Если экземпляр вложенного класса может существовать в отрыве от экземпляра внешнего класса, то вложенный класс не может быть неста­тическим классом-членом: нельзя создать экземпляр нестатического класса-члена, не создав включающего его экземпляра.

Связь между экземпляром нестатического класса-члена и включающим его эк-

земпляроМ устанавливается при создании первого, и после этого поменять ее нельзя. Обычно эта связь задается автоматически путем вызова конструктора нестатического класса-члена из экземпляра метода во внешнем классе. Иногда можно установить связь вручную, используя выражение enclosinglnstance.newMemberClass(args). Как можно предположить, эта связь занимает место в экземпляре нестатического класса­-члена и увеличивает время его создания.

Нестатические классы-члены часто используются для определения адаптера (Adapter) [Сатта95, стр. 139], при содействии которого экземпляр внешнего класса воспринимается своим внутренним классом как экземпляр некоторого класса, не имеющего к нему отношения. Например, в реализациях интерфейса Мар нестатиче­ские классы-члены обычно применяются для создания представлении /(оллекции (collection view), возвращаемых методами keySet, entrySet и values интерфейса Мар. Аналогично, в реализациях интерфейсов коллекций, таких как Set и List, нестатиче­ские классы-члены обычно используются для создания итераторов:

// Типичный вариант использования нестатического класса-члена

public class MySet extends AbstractSet {

// Основная часть класса опущена

publiC Iterator iterator() {

return new Mylterator(); }

private class Mylterator implements Iterator {

}

}

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

 

 

88

 

 

Закрытые статические классы-члены обычно должны представлять составные части объекта, доступ к которым осуществляется через внешний класс. Например, рассмотрим экземпляр класса Мар, который сопоставляет ключи и значения. Внутри экземпляра Мар для каждой пары ключ/значение обычно создается объект Entry. Хотя каждая такая запись ассоциируется со схемой, клиенту не надо обращаться к собственным методам этой записи (geyKey, getValue и setValue). Следовательно, использовать нестатические классы-члены для представления отдельных записей в схеме Мар было бы расточительностью, самое лучшее решение - закрытый статиче­ский класс-член. Если в декларации этой записи вы случайно пропустите модификатор statlc, схема будет работать, но каждая запись будет содержать ненужную ссылку на общую схему, напрасно занимая время и место в памяти.

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

Анонимные классы в языке программирования Java не похожи ни на какие другие.

Анонимный класс не имеет имени. Он не является членом содержащего его класса. Вместо того чтобы быть декларированным с остальными членами класса, он одно­временно декларируется и порождает экземпляр в момент использования. Анонимный класс можно поместить в любом месте программы, где разрешается применять выраже­ния. В зависимости от местоположения анонимный класс ведет себя как статический либо как нестатический класс-член: в нестатическом контексте появляется окружаю­щий его экземпляр.

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

Анонимный класс обычно служит для создания объекта функции (function object), такого как экземпляр класса Comparator. Например, при вызове следующего метода строки в массиве, будут отсортированы по их длине:

// Типичный пример использования анонимного класса

Arrays.sort(args, new Comparator() {

public int compare(Object o1, Object o2) {

return ((String)o1).length() – ((String)o2).length();

} );

 

 

89

 

Другой распространенный случай использования анонимного класса - создание объекта процесса (process object), такого как экземпляры классов Thread, Runnable или ТiтerTask. Третий вариант: в статическом методе генерации (см. метод intArray­AsList в статье 16). Четвертый вариант: инициализация открытого статического поля final, которое соответствует сложному перечислению типов, когда для каждого экземпляра в перечислении требуется отдельный подкласс (см. класс Operation в статье 21). Если, как было рекомендовано ранее, класс Operation будет статическим членом класса Calculator, то отдельные константы класса Operation окажутся дваж­ды вложенными классами:

 

// Типичный пример использования открытого

// статического класса-члена

public class Calculator {

public static abstract class Operation{

 private final String name;

Operation(String пате) { this.name = name; }

public String toString() { return this.name; }

// Выполняет арифметическую операцию, представленную

// данной константой

abstract double eval(double х, double у);

// Дважды вложенные анонимные классы

public static final Operation PLUS = new Operation("+"){

double eval(double х, double у) { return х + у; }

};

public static final Operation MINUS = new Operation("-") {

double eval(double х, double у) {геturn х - у; }

};

public static final Operation TIMES = new Operation("*"){

double eval(double х, double у)  { , return х * у; }

 };

public static final Operation DIVIDE = new Operat1on("/"){

double eval(double х, double у) { return х / у; }

 

                   };

}

// Возвращает результат указанной операции

public double calculate(double х, Operation ор, double у) {

геturn op.eval(x, у);

                   }

       }

 

90

 

 

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

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

 

 

91

 

 

Глава 5


Поделиться:



Последнее изменение этой страницы: 2019-04-11; Просмотров: 231; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.036 с.)
Главная | Случайная страница | Обратная связь