Главная » Java, Советы » Для ссыпки на объект используйте его интерфейс

0

 

В статье 25 дается совет: в качестве типа параметра указывать интерфейс, а не класс. В более общей формулировке: ссылаясь на объект, вы должны отдавать предпочтение не классу, а интерфейсу. Если есть подходящие типы интерфейсов, то параметры, возвращаемые значения, переменные и поля следует декларировать, указывая интерфейс. Единственный случай, когда вам нужно ссылаться на класс объекта,- при его создании. Для пояснения рассмотрим случай с классом Vector, который является реализацией интерфейса List. Возьмите за правило писать так

 

// Хорошо: указывается тип интерфейса.

List subscribers = new Vector();

 

а не так:

 

// Плохо: в качестве типа указан класс!

Vector subscribers = new Vector();

 

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

 

List subscribers = new ArrayList();

 

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

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

 

Есть лишь несколько сложных приложений, которым необходим механизм отражения. В их число входят визуализаторы классов, инспекторы объектов, анализаторы программного кода и интерпретирующие встроенные системы. Отражение можно также использовать в системах вызова удаленных процедур (RPC system) с целью снижения потребности в компиляторах запросов. Если у вас есть сомнения, подпадает ли ваше приложение в одну из этих категорий, вероятнее всего, оно к ним не относится.

Вы можете без больших затрат использовать многие преимущества механизма отражения, если будете применять его в усеченном виде. Во многих программах, которым нужен класс, отсутствовавший на момент компиляции, для ссылки на него можно использовать соответствующий интерфейс или суперкласс (статья 34). Если это то, что вам нужно, вы можете сначала опосредованно создать экземпляры, а затем обращаться к ним обычным образом. используя их интерфейс или суперкласс. Если соответствующий конструктор, как часто бывает, не имеет параметров, вам даже не нужно обращаться к пакету java.lang.ref1ect – требуемые функции предоставит метод C1ass. newlnstance.

В качестве примера приведем программу, которая создает экземпляр интерфейса Set, чей класс задан первым аргументом командной строки. Остальные аргументы командной строки программа вставляет в полученный набор и затем распечатывает его. При выводе аргументов программа уничтожает дубликаты (первый аргумент игнорируется). Порядок печати аргументов зависит от того, какой класс указан в первом аргументе. Если вы указываете "java.util.HashSet", аргументы выводятся в произвольном порядке, если – "java.uti1.TreeSet", они печатаются в алфавитном порядке, поскольку элементы в наборе TreeSet сортируются.

 

// Опосредованное создание экземпляра с доступом

public static void main(String[] args) {

// Преобразует имя класса в экземпляр класса

Class сl = null;

try {

сl =Class.forName(args[0]); }

catch(ClassNotFoundException е) {

System.err.println("Class not found.”);           // Класс не найден

System. exi t (1) ;  }

 

// Создает экземпляр класса

Sets = null;

try {

s = (Set) cl.newlnstance();

catch(IllegalAccessExceptlon е) {

System.err.println("Class not accessible. ");        // Доступ к классу невозможен System.exit(1);

 

 

} catch(InstantiationException е) {

System.err.println("Class not instantiable.");  // Класс не позволяет создать экземпляр System.exit(1); }

// Проверяет набор

s.addAll(Arrays.asList(args).subList(1, args.length-1));

 System.out.println(s); }

 

Хотя эта программа является всего лишь игрушкой, она показывает мощный прием. Этот код легко превратить в универсальную программу про верки наборов, которая проверяет правильность указанной реализации Set, активно воздействуя на один или несколько экземпляров и выясняя, выполняют ли они соглашения для интерфейса Set. Точно так же его можно превратить в универсальный инструмент для анализа производительности. Методика, которую демонстрирует эта программа, в действительности достаточна для создания полноценной инфраструктуры с предоставлением услуг (service provider framework) (статья 1). В большинстве случаев описанный прием это все, что нужно знать об отражении классов.

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

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

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

 

Источник: Джошуа Блох, Java TM Эффективное программирование, Издательство «Лори»

По теме:

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