Java: руководство для начинающих (ЛП) - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Этот интерфейс объявляется открытым (public), а следовательно, он может бытьреализован в классе, принадлежащем любому пакету.Реализация интерфейсов
Определенный один раз интерфейс может быть реализован одним или несколькими классами. Для реализации интерфейса в определение класса следует ввести ключевое слово implements, а затем определить методы, объявленные в интерфейсе. Ниже приведена общая форма реализации интерфейса в классе.class имя_класса extends суперкласс implements интерфейс { // тело класса}
Если в классе должно быть реализовано несколько интерфейсов, то имена интерфейсов указываются через запятую. Разумеется, ключевое слово extends и имя суперкласса указывать не обязательно.
Реализуемые методы интерфейса должны быть объявлены открытыми (public). А сигнатура реализованного метода должна полностью соответствовать сигнатуре, объявленной в составе интерфейса. Ниже приведен пример класса ByTwos, реализующего рассмотренный ранее интерфейс Series. В этом классе формируется последовательный ряд числовых значений, каждое из которых на два больше предыдущего.// Реализация интерфейса Series,class ByTwos implements Series { int start; int val; ByTwos() { start = 0; val = 0; } public int getNext() { val += 2; return val; } public void reset() { start = 0; val = 0; } public void setStart(int x) { start = x; val = x; }}
Обратите внимание на то, что методы getNext (), reset () и setStart () объявлены открытыми. Это нужно сделать непременно, поскольку любой метод интерфейса неявно считается открытым для доступа. Ниже приведен пример программы, демонстрирующий применение класса ByTwos.class SeriesDemo { public static void main(String args[]) { ByTwos ob = new ByTwos(); for(int i=0; i < 5; i++) System.out.println("Next value is " + ob.getNext ()); System.out.println("nResetting"); ob.reset(); for(int i=0; i < 5; i++) System.out.println("Next value is " + ob.getNext()); System.out.println("nStarting at 100"); ob.setStart(100) ; for(int i=0; i < 5; i++) System.out.println("Next value is " + ob.getNext()); }}
Выполнение этой программы дает следующий результат:Next value is 2Next value is 4Next value is 6Next value is 8Next value is 10ResettingNext value is 2Next value is 4Next value is 6Next value is 8Next value is 10Starting at 100Next value is 102Next value is 104Next value is 106Next value is 108Next value is 110
Класс, реализующий интерфейс, может содержать дополнительные переменные и методы, что вполне допустимо. Более того, именно так в большинстве случаев и поступают программирующие на Java. Например, в приведенную ниже версию класса By Twos добавлен метод getPrevious (), возвращающий предыдущее числовое значение.// Реализация интерфейса Series и добавление метода getPrevious().class ByTwos implements Series { int start; int val; int prev; ByTwos() { start = 0; val = 0; prev = -2; } public int getNextO { prev = val; val += 2; return val; } public void reset() { start = 0; val = 0; prev = -2; } public void setStart(int x) { start = x; val = x; prev = x - 2; } // Добавление метода, не объявленного в интерфейсе Series. int getPrevious() { return prev; }}
Обратите внимание на то, что для добавления метода getPrevious () пришлось изменить реализацию методов, объявленных в интерфейсе Series. Но сам интерфейс не претерпел никаких изменений. Эти изменения не видны за пределами класса и не влияют на его использование. В этом и состоит одно из преимуществ интерфейсов.
Как пояснялось ранее, интерфейс может быть реализован каким угодно количеством классов. В качестве примера ниже приведен код класса ByThrees. Этот класс формирует последовательный ряд числовых значений, каждое из которых на три больше предыдущего.// Еще одна реализация интерфейса Series,class ByThrees implements Series { int start; int val; ByThrees() { start = 0; val = 0; } public int getNext () { val += 3; return val; } public void reset () { start = 0; val - 0; } public void setStart(int x) { start = x; val = x; }}
Следует также иметь в виду, что если в определении класса имеется ключевое слово implements, но он не реализует все методы указанного интерфейса, то этот класс должен быть объявлен абстрактным (abstract). Объект такого класса создать нельзя, но его можно использовать в качестве суперкласса, а завершить реализацию методов интерфейса в его подклассах.Применение интерфейсных ссылок
Как это ни покажется странным, но в Java допускается объявлять переменные ссылочного интерфейсного типа, т.е. переменные ссылки на интерфейс. Такая переменная может ссылаться на любой объект, реализующий ее интерфейс. При вызове метода для объекта по интерфейсной ссылке выполняется вариант этого метода, реализованный в классе данного объекта. Этот процесс аналогичен применению ссылки на суперкласс для доступа к объекту подкласса, как пояснялось в главе 7.
Ниже приведен пример программы, демонстрирующий применение интерфейсной ссылки. По такой ссылке в данной программе будут вызываться методы, принадлежащие классам ByTwos и ByThrees.// Применение интерфейсных ссылок,class ByTwos implements Series { int start; int val; ByTwos () { start = 0; val = 0; } public int getNext() { val += 2; return val; } public void reset() { start = 0; val = 0; } public void setStart(int x) { start = x; val = x; }}class ByThrees implements Series { int start; int val; ByThrees() { start = 0; val = 0; } public int getNext() { val += 3; return val; } public void reset() { start = 0; val = 0; } public void setStart(int x) { start = x; val = x; }}class SeriesDemo2 { public static void main(String args[]) { ByTwos twoOb = new ByTwos(); ByThrees threeOb = new ByThrees(); Series ob; for(int i=0; i < 5; i++) { ob = twoOb; // Обращение к объекту по интерфейсной ссылке. System.out.println("Next ByTwos value is " + ob.getNext()); ob = threeOb; // Обращение к объекту по интерфейсной ссылке. System.out.println("Next ByThrees value is " + ob.getNext()); } }}
В методе main () переменная ob объявляется как ссылка на интерфейс Series. Это означает, что в данной переменной может храниться ссылка на любой объект, реализующий интерфейс Series. В данном случае в переменной ob сохраняется ссылка на объекты twoOb и threeOb, т.е. в разные моменты времени переменная представляет собой ссылку на объект класса ByTwos или же на объект класса ByThrees. Оба эти класса реализуют интерфейс Series. Переменная ссылки на интерфейс содержит сведения только о методах, объявленных в этом интерфейсе. Следовательно, переменная ob не может быть использована для доступа к любым другим переменным и методам, которые поддерживаются в объекте, но не объявлены в интерфейсе.
Пример для опробования 8.1.Создание интерфейса для очереди
Для того чтобы продемонстрировать истинные возможности интерфейсов, обратимся к конкретному практическому примеру. В предыдущих главах был создан класс Queue, реализующий простую очередь фиксированного размера для хранения символов. Но обеспечить функционирование очереди можно разными способами. В частности, очередь может быть фиксированного размера или “растущей”, линейной (т.е. переполняться при достижении верхней границы выделенной памяти) или кольцевой (в этом случае при удалении символов из очереди освобождается место для новых элементов). Кроме того, очередь может быть реализована на базе массива, связного списка, двоичного дерева и т.д. Как бы ни была воплощена очередь, интерфейс для нее остается без изменения, т.е. методы put () и get (), определяющие этот интерфейс, выполняют одинаковые действия независимо от внутренней организации очереди. А поскольку интерфейс для очереди не зависит от конкретной ее реализации, его нетрудно определить, а конкретные детали разработать в каждой реализации очереди по отдельности.
В этом проекте предстоит сначала создать интерфейс для очереди, хранящей символы, а затем реализовать его тремя способами. Во всех трех реализациях для хранения символов будет использоваться массив. Одна из очередей будет линейной и фиксированного размера, т.е. такая же, как и реализованная ранее. Вторая очередь будет кольцевой. В кольцевой очереди при достижении границ массива значения индексов будут автоматически изменяться таким образом, чтобы указывать на начало очереди. Таким образом, в кольцевую очередь можно будет поместить любое количество элементов, но при условии своевременного удаления элементов, включенных в нее ранее. И наконец, третья очередь будет динамической. Это означает, что ее размеры будут увеличиваться по мере необходимости.