Использование интерфейсов

Чтобы возможности интерфейсов были понятны, рассмотрим более реальный пример. В предшествующих главах мы разработали класс Stack, который реализует простой стек фиксированного размера. Однако существует множество способов реализации стека. Например, стек может иметь фиксированный размер, либо быть "увеличивающимся". Стек может также храниться в массиве, связанном списке, бинарном дереве и т.п. Независимо от реализации стека его интерфейс остается неизменным. То есть методы push () и pop () определяют интерфейс стека независимо от нюансов реализации. Поскольку интерфейс стека отделен от его реализации, можно без труда определить интерфейс стека, предоставляя реализации определение специфичных особенностей. Рассмотрим два примера.

Вначале создадим интерфейс, который определяет целочисленный стек. Поместим его в файл intStack. java. Этот интерфейс будет использоваться обеими реализациями стека.

// Определение интерфейса целочисленного стека.
interface IntStack {
void push(int item); // сохранение элемента
int pop(); // извлечение элемента
}

Следующая программа создает класс FixedStack, который реализует версию целочисленного стека фиксированной длины:

// Реализация IntStack, использующая область хранения фиксированного размера.
class FixedStack implements IntStack {
private int stck[];
private int tos;
// резервирование и инициализация стека
FixedStack(int size) {
stck = new int[size];
tos = -1;
}
// заталкивание элемента в стек
public void push(int item) {
if(tos==stck.length-1) // использование члена длины стека
System.out.println("Стек полон.");
else
stck[++tos] = item;
}
// выталкивание элемента из стека
public int pop () {
if (tos < 0) {
System.out.println("Стек пуст.">;
return 0;
}
else
return stck[tos—];
class IFTest {
public static void>main(String args[]) {
FixedStack mystac.kl = new FixedStack (5) ;
FixedStack mystack2 = new FixedStack(8) ;
// заталкивание чисел в стек
for(int i=0; i<5; i++) mystackl.push (i) ;
for(int i=0; i<8; i++) mystack2.push(i);
// выталкивание этих чисел из стека
System.out.println ("Стек в mystackl:");
for(int i=0; i<5; i++)
System.out.println(mystackl.pop());
System.out.println("Стек в mystack2:");
for(int i=0; i<8; i++)
System.out.println(mystack2.pop());
}
}

Теперь создадим еще одну реализацию IntStack, которая, используя то же самое определение interface, создает динамический стек. В этой реализации каждый стек создается с начальной длиной. При превышении этой начальной длины размер стека увеличивается. Каждый раз, когда возникает потребность в дополнительном месте, размер стека удваивается.

// Реализация "увеличивающегося" стека.
class DynStack implements IntStack {
private int stck[];
private int tos;
// резервирование и инициализация стека
DynStack(int size) {
stck = new int[size];
tos = -1;
}
// Заталкивание элемента в стек
public void push(int item) {
// если стек полон, резервирование стека большего размера
if(tos==stck.length-1) {
int temp[] = new int[stck.length * 2];
// удвоение размера
for(int i=0; i temp[i] = stck[i];
stck = temp;
stck[++tos] = item;
}
else
stck[++tos] = item;
}
// Выталкивание элемента из стека
public int pop() {
if(tos < 0) {
System.out.println("Стек пуст.");
return 0;
}
else
return stck[tos—];
}
}
class IFTest2 {
public static void main(String args[]) {
DynStack mystackl = new DynStack(5);
DynStack mystack2 = new DynStack(8);
// Эти циклы увеличивают размеры каждого из стеков
for(int i=0; i<12; i++)
mystackl.push(i);
for(int i=0; i<20; i++)
mystack2.push(i);
System.out.println("Стек в mystackl:");
for(int i=0; i<12; i++)
System.out.println(mystackl.pop());
System.out.println("Стек в mystack2:");
for(int i=0; i<20; i++)
System.out.println(mystack2.pop());
}
}

Следующий класс использует обе реализации FixedStack и DynStack. Он выполняет это посредством ссылки на интерфейс. Это означает, что разрешение обращений к методам push () и pop () осуществляется во время выполнения, а не во время компиляции.

/* Создание переменной интерфейса и обращение к стекам через нее.
*/ class IFTest3 {
public static void main(String args[]) {
IntStack mystack; // создание ссылочной переменной интерфейса
DynStack ds = new DynStack(5);
FixedStack fs = new FixedStack(8);
mystack = ds; // загрузка динамического стека
// заталкивание чисел в стек
for(int i=0; i<12; i++) mystack.push(i);
mystack = fs; // загрузка фиксированного стека
for(int i=0; i<8; i++) mystack.push(i);
mystack = ds;
System.out.println("Значения в динамическом стеке:");
for(int i=0; i<12; i++)
System.out.println(mystack.pop());
mystack = fs;
System.out.println("Значения в фиксированном стеке:");
for(int i=0; i<8; i++)
System.out.println(mystack.pop()) ;
}
}

В этой программе mystack — ссылка на интерфейс IntStack. Таким образом, когда она ссылается на переменную ds, программа использует версии методов push () и pop (), определенные реализацией DynStack. Когда же она ссылается на переменную fs, программа использует версии методов push () и pop (), определенные реализацией FixedStack. Как уже было сказано, эти решения принимаются во время выполнения. Обращение к нескольким реализациям интерфейса через ссылочную переменную интерфейса — наиболее мощный метод поддержки полиморфизма времени выполнения Java.




Rambler's Top100