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

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

Внимание! Поскольку в системе 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 () определяется типом объекта, на который переменная с ссылается во время выполнения. Представленный пример очень прост, поэтому вскоре мы приведем еще один, более реальный пример.