Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Абстрактные классы интерфейсы внутренние классы...doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
204.29 Кб
Скачать

Интерфейсы

Для того чтобы освободить интерфейс класса от его реализации, иметь возможность динамического вызова методов во время выполнения, исключив при этом определение метода или набора методов из иерар­хии наследования, в Java применяются интерфейсы. Используя ключевое слово interface, вы можете указать, что класс должен делать, но не как он это делает. Интерфейсы синтаксически подобны классам, но в них нет экземплярных переменных, и их методы объявляются без тела. Практически, это означает, что вы можете определять интерфейсы, которые не делают предположений относительно того, как они реализованы. Как только интерфейс определен, то реализовать его может любое число классов. И наоборот, один класс может реализовать любое число интерфейсов.

Для реализации интерфейса класс должен создать полный набор методов, определенных интерфейсом. Однако каждый класс может сам определять детали своей реализации.

Интерфейсы разработаны для поддержки динамического вызова методов во время выполнения. Они исключают определение метода или набора методов из иерар­хии наследования. Ключевое слово interface позволяет полностью использовать аспект полиморфизма, декларируемый как "один интерфейс, множественные методы".

Определение интерфейса

Определение интерфейса во многом подобно определению класса. Общая форма интерфейса выглядит так:

access interface name {

return-type method-name1 (parameter-list) ;

return-type method-name2 (parameter-list) ;

type final-varname1 = value;

type final-varname2 = value;

// . . .

return-type method-nameN(parameter-list) ;

type final-varnameN = value;

}

Здесь access — спецификатор доступа (или public или не используется). Если никакой спецификатор доступа не включен, тогда используется доступ по умолчанию, и интерфейс доступен только другим членам пакета, в кото­ром он объявлен. При объявлении с public интерфейс может использовать­ся любым другим кодом, name — имя интерфейса, им может быть любой до­пустимый идентификатор. Объявленные методы не имеют тел. Они заканчиваются точкой с запятой после списка параметров. Это, по существу, абстрактные методы. В пределах интерфейса для них нет никаких умалчиваемых реализаций. Каждый класс, который включает ин­терфейс, должен реализовать все его методы.

Внутри объявлений интерфейсов можно объявлять переменные. Они неявно считаются final и static (это означает, что они не могут быть изменены реализующим классом), а также должны быть инициализированы постоян­ными значениями.

Пример определения интерфейса (Папка 3\Интерфейсы по Шилдту):

interface IntStack {

void push(int item); // запомнить элемент

int pop(); // извлечь элемент

}

Реализация интерфейсов

Когда интерфейс определен, он может реализовываться одним или несколь­кими классами. Для реализации интерфейса в определение класса включают предложение с ключевым словом implements и затем создают методы, опре­деленные в интерфейсе. Общая форма класса, который включает implements предложение, выглядит так:

access class classname [extends superclass]

[implements interface [,interface...] ] {

// тело-класса

}

Здесь access — спецификатор доступа (public или не используется). Если класс реализует более одного интерфейса, они разделяются запятой.

Например (Папка 3\Интерфейсы по Шилдту):

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--]; }

}

При реализации метода интерфейса он должен быть объявлен как public.

Для классов, которые реализуют интерфейсы, допустимо определять дополнительные собственные члены.

Реализации доступа через интерфейсные ссылки

Можно объявлять переменные как объектные ссылки, которые используют интерфейсный тип, а не тип класса. В такой переменной можно сохранять всякий экземпляр любого класса, который реализует объявленный интер­фейс. Когда вы вызываете метод через ссылку такого рода, будет вызываться его правильная версия, основанная на актуальном экземпляре интерфейса. Выполняемый метод оты­скивается динамически (во время выполнения), что позволяет создавать классы позже кода, который вызывает их методы. Кодом вызова можно управлять через интерфейс, ничего не зная об объекте вызова. Этот процесс подобен использованию ссылки суперкласса для доступа к объекту подклас­са, но без использования механизма наследования.

Частичные реализации

Если класс включает интерфейс, но полностью не реализует методы, опре­деленные этим интерфейсом, то этот класс должен быть объявлен как abstract (абстрактный).

Любой класс, который наследует такой класс, должен реализо­вать все методы, или объявить себя как abstract.

Применения интерфейсов

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

Ниже показан интерфейс, определяющий целый стек. Поместим его в фай­ле с именем IntStack.java. Этот интерфейс будет использоваться обеими реа­лизациями стека.

// Определение интерфейса целого стека.

interface IntStack {

void push(int item); // запомнить элемент

int pop(); // извлечь элемент

}

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

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--]; }

}

Код представленный ниже демонстрирует использование этого класса

public class IFTest {

public static void main(String args[]) {

FixedStack mystackl = 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());

}

}

Далее следует другая реализация стека, которая создает динамический стек, используя то же самое его определение. В ней каждый стек создается с некоторой исходной длиной. Если эта исходная длина превышена, то стек увеличивается в размере. Каждый раз, когда необходим больший участок памяти, размер стека удваивается.

// Реализует "растущий" стек.

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<stck.length; 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--]; }

}

Код представленный ниже демонстрирует использование этого класса

сlass 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() ) ; }

}

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

/* Создать интерфейсную переменную

и обратиться к стекам через нее. */

class IFTest3 {

public static void main(String args []) {