АБОБА. Справочник по программированию на Java Методическое пособие
Скачать 242.41 Kb.
|
Определение интерфейсаОпределение интерфейса во многом подобно определению класса. Общая форма интерфейса имеет следующий вид: доступ interface имя { возвращаемый_тип имя_метода1(список_параметров); возвращаемый_тип имя_метода2(список_параметров); тип имя_конечной_переменной1 = значение; тип имя_конечной_переменной2 = значение; // ... Возвращаемый_тип имя_методаN(список_параметров); тип имя_конечной_переменнойN = значение; } Если определение не содержит никакого спецификатора доступа, используется доступ по умолчанию, и интерфейс доступен только другим членам того пакета, в котором он объявлен. Если интерфейс объявлен как public, он может быть использован любым другим кодом. В этом случае интерфейс должен быть единственным общедоступным интерфейсом, объявленным в файле, и имя файла должно совпадать с именем интерфейса. Имя – имя интерфейса, которым может быть любой допустимый идентификатор. Обратите внимание, что объявляемые методы не содержат тел. Их объявления завершаются списком параметров, за которым следует символ точки с запятой. По сути, они представляют собой абстрактные методы. Ни один из указанных внутри интерфейса методов не может обладать никакой заданной по умолчанию реализацией. Каждый класс, который включает в себя интерфейс, должен реализовать все его методы. Переменные могут быть объявлены внутри объявлений интерфейсов. Они неявно объявляются как final и static – т.е. реализующий класс не может их изменять. Кроме того, они должны быть также инициализированы. Все методы и переменные неявно объявляются как public. Ниже приведен пример определения интерфейса. В нем объявляется простой интерфейс, который содержит один метод callback(), принимающий единственный целочисленный параметр. interface Callback { void callback(int param); } Реализация интерфейсовКак только интерфейс определен, его может реализовать один или более классов. Чтобы реализовать интерфейс, в определение класса потребуется включить конструкцию implements, а затем создать методы, определенные интерфейсом. Общая форма класса, который содержит выражение implements, имеет следующий вид: доступ class имя_класса [extends суперкласс] [implements интерфейс [,интерфейс...]] { // тело_класса } Если класс реализует более одного интерфейса, имена интерфейсов разделяются запятыми. Если класс реализует два интерфейса, которые объявляют один и тот же метод, то один и тот же метод будет использоваться клиентами любого интерфейса. Методы, которые реализуют интерфейс, должны быть объявлены как public. Кроме того, сигнатура типа реализующего метода должна в точности совпадать с сигнатурой типа, указанной в определении interface. Рассморим небольшой пример класса, который реализует приведенный ранее интерфейс Callback. class Client implements Callback { // Реализует интерфейс Callback public void callback(int p) { System.out.println("Метод callback, вызванный со значением " + p); } } Обратите внимание, что метод callback() объявлен с использованием спецификатора доступа public. При реализации метода интерфейса он должен быть объявлен как public. Вполне допустима и достаточно распространена ситуация, когда классы, которые реализуют интерфейсы, определяют собственные дополнительные члены. Например, следующая версия класса Client реализует метод callback() и добавляет метод nonInfaceMeth(): class Client implements Callback { // Реализует интерфейс Callback public void callback(int p) { System.out.println("Метод callback, вызванный со значением " + p); } void nonIfaceMeth() { System.out.println("Классы, которые реализуют интерфейсы" + "могут определять также и другие члены."); } } Доступ к реализациям через ссылки на интерфейсыПеременные можно объявлять как объектные ссылки, которые используют тип интерфейса, а не тип класса. Посредством такой переменной можно ссылаться на любой экземпляр любого класса, реализующего объявленный интерфейс. При вызове метода с помощью одной из таких ссылок выбор нужной версии будет производиться в зависимости от конкретного экземпляра интерфейса, на который выполняется ссылка. Это – одна из главных особенностей интерфейсов. Поиск выполняемого метода осуществляется динамически во время выполнения, что позволяет создавать классы позже, чем код, который вызывает методы по отношению к этим классам. Диспетчеризация кода может выполняться посредством интерфейса без необходимости наличия каких-либо сведений о “вызывающем”. Этот процесс аналогичен использованию ссылки на суперкласс для доступа к объекту подкласса. Поскольку в системе 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 ссылается во время выполнения. Представленный пример очень прост, поэтому вскоре мы приведем еще один, более реальный пример. |