Доступ к реализациям через ссылки на интерфейс
Переменные можно объявлять как объектные ссылки, которые используют тип интерфейса, а не тип класса. Посредством такой переменной можно ссылаться на любой экземпляр любого класса, реализующего объявленный интерфейс. При вызове метода с помощью одной из таких ссылок выбор нужной версии будет производиться в зависимости от конкретного экземпляра интерфейса, на который выполняется ссылка. Это — одна из главных особенностей интерфейсов. Поиск выполняемого метода осуществляется динамически во время выполнения, что позволяет создавать классы позже, чем код, который вызывает методы по отношению к этим классам. Диспетчеризация кода может выполняться посредством интерфейса без необходимости наличия каких-либо сведений о "вызывающем". Этот процесс аналогичен использованию ссылки на суперкласс для доступа к объекту подкласса.
Внимание! Поскольку в системе Java динамический поиск методов во время выполнения сопряжен со значительными накладными расходами по сравнению с обычным вызовом методов, в коде, для которого важна производительность, интерфейсы следует использовать только тогда, когда это действительно необходимо.
В следующем примере метод callback () вызывается через ссылочную переменную интерфейса:
class Testlface {
public static void main(String args[]) {
Callback с = new Client();
c.callback(42);
}
}
Эта программа создает следующий вывод:
Метод callback, вызванный со значением 42
Обратите внимание, что хотя переменная с объявлена с типом интерфейса Callback, ей был присвоен экземпляр класса Client. Хотя переменную с можно использовать для доступа к методу callback (), она не имеет доступа к каким-то другим членам класса Client. Ссылочная переменная интерфейса располагает сведениями только о тех методах, которые объявлены ее объявлением interface. Таким образом, переменная с не может применяться для доступа к методу nonlf aceMeth (), поскольку она объявлена классом Client, а не классом Callback.
Хотя приведенный пример формально показывает, как ссылочная переменная интерфейса может получать доступ к объекту реализации, он не демонстрирует полиморфные возможности такой ссылки. Чтобы продемонстрировать пример такого применения, вначале создадим вторую реализацию интерфейса Callback:
// Еще одна реализация интерфейса Callback.
class AnotherClient implements Callback {
// Реализация интерфейса Callback
public void callback(int p) {
System.out.println("Еще одна версия callback");
System.out.println("p в квадрате равно " + (p*p));
}
}
Теперь проверим работу следующего класса:
class Testlface2 {
public static void main(String args[]) {
Callback с = new Client();
AnotherClient ob = new AnotherClient();
c.callback(42);
с = ob; // теперь с ссылается на объект AnotherClient
с.callback(42);
}
}
Эта программа создает следующий вывод:
callback вызванный со значением 42
Еще одна версия callback р в квадрате равно 1764
Как видите, вызываемая версия метода callback () определяется типом объекта, на который переменная с ссылается во время выполнения. Представленный пример очень прост, поэтому вскоре мы приведем еще один, более реальный пример.