Выбрать главу

class MyClass implements Containment { // Допустимо Вас теперь вряд ли удивит, что один или несколько параметров типа для универсального интерфейса могут быть ограничены. Это позволяет указывать, какие именно типы данных допустимы для интерфейса. Так, если требуется запретить передачу интерфейсу Containment значений, не являющихся числовыми, для этой цели интерфейс можно объявить следующим образом:

interface Containment { Теперь любой класс, реализующий интерфейс Containment, должен передавать ему значение типа, удовлетворяющее указанным выше ограничениям. Например, класс MyClass, реализующий данный интерфейс, должен объявляться следующим образом:

class MyClass implements Containment { Обратите особое внимание на то, как параметр типа Т объявляется в классе MyClass, а затем передается интерфейсу Containment. На этот раз интерфейсу Containment требуется тип, расширяющий тип Number, поэтому в классе MyClass, реализующем этот интерфейс, должны быть указаны соответствующие ограничения. Если верхняя граница задана в объявлении класса, то ее нет необходимости указывать еще раз в операторе implements. Если же попытаться сделать это, будет получено сообщение об ошибке. Например, следующее выражение составлено неверно и не будет скомпилировано:

// Ошибка! class MyClass implements Containment { Если параметр типа задан в объявлении класса, он лишь передается интерфейсу без дальнейших видоизменений. Ниже приведена общая форма объявления обобщенного интерфейса.

interface имяинтерфейса<параметрытипа> { // ... где параметры_типа указываются списком через запятую. При реализации обобщенного интерфейса в объявлении класса также должны быть указаны параметры типа. Общая форма объявления класса, реализующего обобщенный интерфейс, приведена ниже.

class имякласса<параметрытипа> implements имяинтерфейса<параметрытипа> { **Пример для опробования 13.1.** Создание обобщенного класса очереди Главным преимуществом обобщенных классов является возможность создания надежного кода, пригодного для повторного использования. Как пояснялось в начале главы, многие алгоритмы могут быть реализованы одинаково независимо от типа данных. Например, очередь в равной степени пригодна для хранения целых чисел, символьных строк, объектов типа File и других типов данных. Вместо того чтобы создавать отдельный класс очереди для объектов каждого типа, можно разработать единое обобщенное решение, пригодное для обращения с объектами любого типа. В итоге цикл проектирования, программирования, тестирования и отладки кода будет выполняться только один раз, не повторяясь всякий раз, когда потребуется организовать очередь для нового типа данных. В этом проекте предстоит в очередной и последний раз видоизменить класс очереди, разработка которого была впервые начата в главе 5. Для этой цели будет объявлен обобщенный интерфейс, определяющий операции над очередью, созданы два класса исключений и реализована очередь фиксированного размера. Разумеется, вам ничто не помешает поэкспериментировать с другими разновидностями обобщенных очередей, например, создать динамическую или циклическую очередь, следуя приведенным ниже рекомендациям. Кроме того, исходный код, реализующий очередь в этом проекте, будет организован в виде ряда отдельных файлов. С этой целью код интерфейса, исключений, реализации очереди фиксированного размера и программы, демонстрирующей очередь в действии, будет распределен по отдельным исходным файлам. Такая организация исходного кода отвечает подходу, принятому в работе над большинством реальных проектов. Последовательность действий 1. Первым этапом создания обобщенной очереди станет формирование обобщенного интерфейса, описывающего две операции над очередью: размещение и извлечение. Обобщенная версия интерфейса очереди будет называться iGenQ, ее исходный код приведен ниже. Введите этот код во вновь созданный файл IGenQ. java. // Обобщенный интерфейс очереди, public interface IGenQ<T> { // поместить элемент в очередь void put(T ch) throws QueueFullException; // извлечь элемент из очереди Т get() throws QueueEmptyException; } ``` Обратите внимание на то, что тип данных, предназначенных для хранения в очереди, определяется параметром типа т.

Далее создайте файл QExc. j ava. Введите в него два приведенных ниже класса, в которых определяются исключения, возникающие в работе с очередью. // Исключение в связи с ошибками переполнения очереди, class QueueFullException extends Exception { int size; QueueFullException(int s) { size = s; } public String toString() { return "\nQueue is full. Maximum size is " + size; } } // Исключение в связи с ошибками опустошения очереди, class QueueEmptyException extends Exception { public String toString() { return "\nQueue is empty."; } }

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

Создайте файл GenQueue.java. Введите в него приведенный ниже код, в котором реализуется очередь фиксированного размера. // Обобщенный класс, реализующий очередь фиксированного размера, class GenQueue<T> implements IGenQ<T> { private T q[]; // Массив для хранения элементов очереди, private int putloc, getloc; // Индексы размещения и извлечения // элементов очереди. // построить пустую очередь из заданного массива public GenQueue(Т[] aRef) { q = aRef; putloc = getloc = 0; } // поместить элемент в очередь public void put(Т obj) throws QueueFullException { if(putloc==q.length-1) throw new QueueFullException(q.length-1); putloc++; q[putloc] = obj; } // извлечь элемент из очереди public Т get() throws QueueEmptyException { if(getloc == putloc) throw new QueueEmptyException(); getloc++; return q[getloc]; } }

Класс GenQueue объявляется как обобщенный с параметром типа Т. Этот параметр определяет тип данных, хранящихся в очереди. Обратите внимание на то, что параметр типа Т также передается интерфейсу iGenQ.

Конструктору GenQueue передается ссылка на массив, используемый для хранения элементов очереди. Следовательно, для построения объекта класса GenQueue нужно сначала сформировать массив, тип которого будет совместим с типом объектов, сохраняемых в очереди, а его размер достаточен для размещения этих объектов в очереди. В рассматриваемом здесь коде первый элемент массива не используется, поэтому длина массива должна быть на один элемент больше, чем количество элементов, которые допускается хранить в очереди. Например, в следующих строках кода демонстрируется создание очереди для хранения символьных строк: String strArray[] = new String[10]; GenQueue<String> strQ = new GenQueue<String>(strArray);

Создайте файл GenQDemo.java и введите в него приведенный ниже код. В этом коде демонстрируется работа обобщенной очереди. /* Проект 13.1. Демонстрация обобщенного класса очереди. */ class GenQDemo { public static void main(String args[]) { // создать очередь для хранения целых чисел Integer iStoref] = new Integer[10]; GenQueue<Integer> q = new GenQueue<Integer>(iStore); Integer iVal; System.out.println("Demonstrate a queue of Integers."); try { for(int i=0; i < 5; i++) { System.out.println("Adding " + i + " to the q."); q.put(i); // ввести целочисленное значение в очередь q } } catch (QueueFullException exc) { System.out.println(exc) ; } System.out.println(); try { for(int i=0; i < 5; i++) { System.out.print("Getting next Integer from q: "); iVal = q.get(); System.out.println(iVal); } } catch (QueueEmptyException exc) { System.out.println(exc); } System.out.println() ; // создать очередь для хранения чисел с плавающей точкой Double dStore[] = new Double[10]; GenQueue<Double> q2 = new GenQueue<Double>(dStore); Double dVal; System.out.println("Demonstrate a queue of Doubles."); try { for(int i=0; i < 5; i++) { System.out.println("Adding " + (double)i/2 + " to the q2."); q2.put((double)i/2); // ввести значение типа double в очередь q2 } } catch (QueueFullException exc) { System.out.println(exc); } System.out.println(); try { for(int i=0; i < 5; i++) { System.out.print("Getting next Double from q2: "); dVal = q2.get (); System.out.println(dVal); } } catch (QueueEmptyException exc) { System.out.println(exc); } } }