Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Доступ к реализациям через ссылки на интерфейсы




Доступ к реализациям через ссылки на интерфейсы

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

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

В следующем примере метод callback() вызывается через ссылочную переменную интерфейса:

class TestIface {

public static void main(String args[]) {

Callback c = new Client();

c. callback(42);

}

}

Эта программа создает следующий вывод:

Метод callback, вызванный со значением 42

Обратите внимание, что хотя переменная c объявлена с типом интерфейса Callback, ей был присвоен экземпляр класса Client. Хотя переменную c можно использовать для доступа к методу callback(), она не имеет доступа к каким-то другим членам класса Client. Ссылочная переменная интерфейса располагает сведениями только о тех методах, которые объявлены ее объявлением interface. Таким образом, переменная c не может применяться для доступа к методу nonIfaceMeth(), поскольку она объявлена классом Client, а не классом Callback.

Хотя приведенный пример формально показывает, как ссылочная переменная интерфейса может получать доступ к объекту реализации, он не демонстрирует полиморфные возможности такой ссылки. Чтобы продемонстрировать пример такого применения, вначале создадим вторую реализацию интерфейса Callback:

// Еще одна реализация интерфейса Callback.

class AnotherClient implements Callback {

// Реализация интерфейса Callback

BookNew_JAVA-7. indb 226 02. 06. 2007 1: 06: 57

Глава 9. Пакеты и интерфейсы 227

public void callback(int p) {

System. out. println(" Еще одна версия callback" );

System. out. println(" p в квадрате равно " + (p*p));

}

}

Теперь проверим работу следующего класса:

class TestIface2 {

public static void main(String args[]) {

Callback c = new Client();

AnotherClient ob = new AnotherClient();

c. callback(42);

c = ob; // теперь c ссылается на объект AnotherClient

c. callback(42);

}

}

Эта программа создает следующий вывод:

callback вызванный со значением 42

Еще одна версия callback

p в квадрате равно 1764

Как видите, вызываемая версия метода callback() определяется типом объекта, на который переменная c ссылается во время выполнения. Представленный пример очень прост, поэтому вскоре мы приведем еще один, более реальный пример.

Частичные реализации

Если класс содержит интерфейс, но не полностью реализует определенные им методы, он должен быть объявлен как abstract (абстрактный). Например:

abstract class Incomplete implements Callback {

int a, b;

void show() {

System. out. println(a + " " + b);

}

//...

}

В этом примере класс Incomplete не реализует метод callback() и должен быть объявлен как абстрактный. Любой класс, который наследует Incomplete, должен реализовать метод callback() либо быть также объявленным как abstract.

Вложенные интерфейсы

Интерфейс может быть объявлен членом класса или другого интерфейса. Такой интерфейс называется интерфейсом-членом или вложенным интерфейсом. Вложенный интерфейс может быть объявлен как public, private или protected. Это отличает его от интерфейса верхнего уровня, который должен быть либо объявлен как public, либо, как уже было отмечено, должен использовать уровень доступа, заданный по умолчанию.

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

Переменные в интерфейсах

Интерфейсы можно применять для импорта совместно используемых констант в несколько классов посредством простого объявления интерфейса, который содержит переменные, инициализированные нужными значениями. При включении интерфейса в класс (т. е. при “реализации” интерфейса) имена всех этих переменных будут помещены в область констант. (Это аналогично использованию в программе C/C++ заголовочного файла для создания большого числа констант типа #define или объявлений const. )

Если интерфейс не содержит никаких методов, любой класс, который включает в себя такой интерфейс, в действительности ничего не реализует. Это равносильно тому, что класс импортировал бы постоянные поля в пространство имен класса в качестве переменных типа final. В следующем примере эта технология применяется для реализации автоматизированной “системы принятия решений”.

import java. util. Random;

interface SharedConstants {

int NO = 0;

int YES = 1;

int MAYBE = 2;

int LATER = 3;

int SOON = 4;

int NEVER = 5;

}

class Question implements SharedConstants {

Random rand = new Random();

int ask() {

int prob = (int) (100 * rand. nextDouble());

if (prob < 30)

return NO; // 30%

else if (prob < 60)

return YES; // 30%

else if (prob < 75)

return LATER; // 15%

else if (prob < 98)

return SOON; // 13%

else

return NEVER; // 2%

}

}

class AskMe implements SharedConstants {

static void answer(int result) {

switch(result) {

case NO:

System. out. println(" Нет" );

break;

case YES:

System. out. println(" Да" );

break;

case MAYBE:

System. out. println(" Возможно" );

break;

case LATER:

System. out. println(" Позднее" );

break;

case SOON:

System. out. println(" Вскоре" );

break;

case NEVER:

System. out. println(" Никогда" );

break;

}

}

public static void main(String args[]) {

Question q = new Question();

answer(q. ask());

answer(q. ask());

answer(q. ask());

answer(q. ask());

}

}

Обратите внимание, что в этой программе использован один из стандартных Javaклассов – Random. Этот класс генерирует псевдослучайные числа. Он содержит несколько методов, которые позволяют получать случайные числа в требуемой программой форме. В этом примере применяется метод nextDouble(), который возвращает случайные числа в диапазоне от 0, 0 до 1, 0.

В приведенном примере программы два класса Question и AskMe реализуют интерфейс SharedConstants, в котором определены константы NO (Нет), YES (Да), MAYBE (Возможно), SOON (Вскоре), LATER (Позднее) и NEVER (Никогда). Код внутри каждого класса ссылается на эти константы так, как если бы каждый класс определял или наследовал их непосредственно.

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...