21
new FilterAdapter(new BandPass(3.0. 4 0)). w);
}
} /* Output.
Используем Processor LowPass Waveform 0
Используем Processor HighPass Waveform 0
Используем Processor BandPass
Waveform 0 *///.-
Конструктор FilterAdapter получает исходный интерфейс (Filter) и создает объект с требуемым интерфейсом Processor. Также обратите внимание на применение делегирования в классе FilterAdapter.
Отделение интерфейса от реализации позволяет применять интерфейс к разным реализациям, а следовательно, расширяет возможности повторного использования кода.
«Множественное наследование» в Java
Так как интерфейс по определению не имеет реализации (то есть не обладает памятью для хранения данных), нет ничего, что могло бы помешать совмещению нескольких интерфейсов. Это очень полезная возможность, так как в некоторых ситуациях требуется выразить утверждение: «Икс являетсяиА,иБ,иВ одновременно». В С++ подобное совмещение интерфейсов нескольких классов называетсямножественным наследованием, и оно имеет ряд очень неприятных аспектов, поскольку каждый класс может иметь свою реализацию. В Java можно добиться аналогичного эффекта, но, поскольку реализацией обладает всего один класс, проблемы, возникающие при совмещении нескольких интерфейсов в С++, в Java принципиально невозможны:
При наследовании базовый класс вовсе не обязан быть абстрактным или «реальным» (без абстрактных методов). Если наследованиедействительноосуществляется не от интерфейса, то среди прямых «предков» класс может быть только один — все остальные должны быть интерфейсами. Имена интерфейсов перечисляются вслед за ключевым словом implements и разделяются запятыми. Интерфейсов может быть сколько угодно, причем к ним можно проводить восходящее преобразование. Следующий пример показывает, как создать новый класс на основе реального класса и нескольких интерфейсов:
//: interfaces/Adventure java
// Использование нескольких интерфейсов.
interface CanFight { void fightO,
}
interface CanSwim { void swimO,
}
interface CanFly { void fly().
}
class ActionCharacter {
public void fightO {}
}
class Hero extends ActionCharacter
implements CanFight, CanSwim, CanFly { public void swimO {}
public void fly() {}
}
public class Adventure {
public static void t(CanFight x) { x fightO; } public static void u(CanSwim x) { x swimO, } public static void v(CanFly x) { x fly(); } public static void w(ActionCharacter x) { x.fightO. } public static void main(String[] args) { Hero h = new HeroO;
t(h), // Используем объект в качестве типа CanFight u(h). // Используем объект в качестве типа CanSwim v(h). // Используем объект в качестве типа CanFly w(h), // Используем объект в качестве ActionCharacter
}
} ///-
Мы видим, что класс Него сочетает реальный класс ActionCharacter с интерфейсами CanFight, CanSwim и CanFly. При объединении реального класса с интерфейсами на первом месте должен стоять реальный класс, а за ним следуют интерфейсы (иначе компилятор выдаст ошибку).
Заметьте, что объявление метода fight() в интерфейсе CanFight совпадает с тем, что имеется в классе ActionCharacter, и поэтому в классе Негонетопределения метода fight(). Интерфейсы можно расширять, но при этом получается другой интерфейс. Необходимым условием для создания объектов нового типа является наличие всех определений. Хотя класс Него не имеет явного определения метода fight(), это определение существует в классе ActionCharacter, что и делает возможным создание объектов класса Него.
Класс Adventure содержит четыре метода, которые принимают в качестве аргументов разнообразные интерфейсы и реальный класс. Созданный объект Него передается всем этим методам, а это значит, что выполняется восходящее преобразование объекта к каждому интерфейсу по очереди. Система интерфейсов Java спроектирована так, что она нормально работает без особых усилий со стороны программиста.
Помните, что главная причина введения в язык интерфейсов представлена в приведенном примере: это возможность выполнять восходящее преобразование к нескольким базовым типам. Вторая причина для использования интерфейсов совпадает с предназначением абстрактных классов: запретить програм- мисту-клиенту создание объектов этого класса.
Возникает естественный вопрос: что лучше — интерфейс или абстрактный класс? Если можно создать базовый класс без определений методов и переменных-членов, выбирайте именно интерфейс, а не абстрактный класс. Вообще говоря, если известно, что нечто будет использоваться как базовый класс, первым делом постарайтесь сделать это «нечто» интерфейсом.
Расширение интерфейса через наследование
Наследование позволяет легко добавить в интерфейс объявления новых методов, а также совместить несколько интерфейсов в одном. В обоих случаях получается новый интерфейс, как показано в следующем примере:
//• interfaces/HorrorShow java
// Расширение интерфейса с помощью наследования
interface Monster { void menace(),
}
interface DangerousMonster extends Monster { void destroy();
}
interface Lethal { void kill();
}
class DragonZilla implements DangerousMonster { public void menaceО {} public void destroyО {}
}
interface Vampire extends DangerousMonster, Lethal { void drinkBloodO;
}
class VeryBadVampire implements Vampire { public void menaceO {} public void destroyО {} public void killO {} public void drinkBloodO {}
}
public class HorrorShow {
static void u(Monster b) { b.menaceO; } static void v(DangerousMonster d) { d. menaceO, d.destroyО;
}
static void w(Lethal 1) { 1 killO; } public static void main(String[] args) {
DangerousMonster barney = new DragonZi11a(); u(barney); v(barney);
Vampire vlad = new VeryBadVampire(); u(vlad), v(vlad); w(vlad);
}
} ///:-
DangerousMonster представляет собой простое расширение Monster, в результате которого образуется новый интерфейс. Он реализуется классом DragonZilla.
Синтаксис, использованный в интерфейсе Vampire, работаеттолькопри наследовании интерфейсов. Обычно ключевое слово extends может использоваться всего с одним классом, но, так как интерфейс можно составить из нескольких других интерфейсов, extends подходит для написания нескольких имен интерфейсов при создании нового интерфейса. Как нетрудно заметить, имена нескольких интерфейсов разделяются при этом запятыми.