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

Интерфейсы

Интерфейсы и абстрактные классы улучшают структуру кода и способствуют отделению интерфейса от реализации.

В традиционных языках программирования такие механизмы не получили особого распространения. Например, в С++ существует лишь косвенная под­держка этих концепций. Сам факт их существования в Java показывает, что эти концепции были сочтены достаточно важными для прямой поддержки в языке.

Мы начнем с понятия абстрактного класса, который представляет собой своего рода промежуточную ступень между обычным классом и интерфейсом. Абстрактные классы — важный и необходимый инструмент для создания клас­сов, содержащих нереализованные методы. Применение «чистых» интерфейсов возможно не всегда.

Абстрактные классы и методы

В примере с классами музыкальных инструментов из предыдущей главы мето­ды базового класса Instrument всегда оставались «фиктивными». Попытка вызо­ва такого метода означала, что в программе произошла какая-то ошибка. Это объяснялось тем, что класс Instrument создавался для определения общего ин­терфейса всех классов, производных от него.

В этих примерах общий интерфейс создавался для единственной цели— его разной реализации в каждом производном типе. Интерфейс определяет базовую форму, общность всех производных классов. Такие классы, как Instrument, также называют абстрактными базовыми классами или просто абстрактными классами

Если в программе определяется абстрактный класс вроде Instrument, созда­ние объектов такого класса практически всегда бессмысленно. Абстрактный класс создается для работы с набором классов через общий интерфейс. А если Instrument только выражает интерфейс, а создание объектов того класса не име­ет смысла, вероятно, пользователю лучше запретить создавать такие объекты. Конечно, можно заставить все методы Instrument выдавать ошибки, но в этом случае получение информации откладывается до стадии выполнения. Ошибки такого рода лучше обнаруживать во время компиляции.

В языке Java для решения подобных задач применяются абстрактные мето­ды1. Абстрактный метод незавершен; он состоит только из объявления и не имеет тела. Синтаксис объявления абстрактных методов выглядит так:

abstract void f();

Класс, содержащий абстрактные методы, называется абстрактным классом. Такие классы тоже должны помечаться ключевым словом abstract (в противном случае компилятор выдает сообщение об ошибке).

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

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

Класс Instrument очень легко можно сделать абстрактным. Только некоторые из его методов станут абстрактными, поскольку объявление класса как abstract не подразумевает, что все его методы должны быть абстрактными. Вот что по­лучится:

А вот как выглядит реализация примера оркестра с использованием абст­рактных классов и методов:

//. interfaces/music4/Musiс4 java // Абстрактные классы и методы package interfaces.music4; import polymorphism.music.Note, import static net mindview util Print *.

abstract class Instrument {

private int i; // Память выделяется для каждого объекта public abstract void play(Note n); public String whatO { return "Instrument"; } public abstract void adjustO,

}

class Wind extends Instrument { public void play(Note n) {

print("Wind playО " + n),

}

public String whatO { return "Wind"; } public void adjustO {}

}

class Percussion extends Instrument { public void play(Note n) {

printC'Percussion playO " + n).

}

public String whatO { return "Percussion", } public void adjustO {}

}

class Stringed extends Instrument { public void play(Note n) {

print ("Stringed playO " + n),

}

public String whatO { return "Stringed". } public void adjustO {}

}

class Brass extends Wind {

public void play(Note n) {

printCBrass.playO " + n);

}

public void adjustO { printC'Brass adjustO"), }

}

class Woodwind extends Wind { public void play(Note n) {

print("Woodwind playО " + n);

}

public String whatO { return "Woodwind", }

}

public class Music4 {

// Работа метода не зависит от фактического типа объекта. // поэтому типы, добавленные в систему, будут работать правильно:

static void tune(Instrument i) {

//

i.piay(Note MIDDLE_C),

}

static void tuneAll (Instrument!!] e) { for(Instrument i e) tune(i).

}

public static void main(String[] args) {

// Восходящее преобразование при добавлении в массив Instruments orchestra = { new WindO. new PercussionO. new StringedO. new BrassO. new WoodwindО

}.

tuneAl1(orchestra).

}

} /* Output Wind.pi ayО MIDDLE_C Percussion playO MIDDLE_C Stringed playO MIDDLE_C Brass playO MIDDLE_C Woodwind pi ayО MIDDLE_C */// ~

Как видите, объем изменений минимален.

Создавать абстрактные классы и методы полезно, так как они подчеркивают абстрактность класса, а также сообщают и пользователю класса, и компилятору, как следует с ним обходиться. Кроме того, абстрактные классы играют полез­ную роль при переработке программ, потому что они позволяют легко переме­щать общие методы вверх по иерархии наследования.

Интерфейсы

Ключевое слово interface становится следующим шагом на пути к абстракции. Оно используется для создания полностью абстрактных классов, вообще не имеющих реализации. Создатель интерфейса определяет имена методов, списки аргументов и типы возвращаемых значений, но не тела методов.

Ключевое слово interface фактически означает: «Именно так должны выгля­деть все классы, которые реализуют данный интерфейс». Таким образом, лю­бой код, использующий конкретный интерфейс, знает только то, какие методы вызываются для этого интерфейса, но не более того. Интерфейс определяет своего рода «протокол взаимодействия» между классами.

Однако интерфейс представляет собой нечто большее, чем абстрактный класс в своем крайнем проявлении, потому что он позволяет реализовать подо­бие «множественного наследования» С++: иначе говоря, создаваемый класс мо­жет быть преобразован к нескольким базовым типам.

Чтобы создать интерфейс, используйте ключевое слово interface вместо class. Как и в случае с классами, вы можете добавить перед словом interface специфи­катор доступа public (но только если интерфейс определен в файле, имеющем то же имя) или оставить для него дружественный доступ, если он будет исполь­зоваться только в пределах своего пакета. Интерфейс также может содержать поля, но они автоматически являются статическими (static) и неизменными (final).

Для создания класса, реализующего определенный интерфейс (или группу интерфейсов), используется ключевое слово implements. Фактически оно озна­чает: «Интерфейс лишь определяет форму, а сейчас будет показано, как это ра­ботает». В остальном происходящее выглядит как обычное наследование. Рас­смотрим реализацию на примере иерархии классов Instrument: