21

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 подходит для написания нескольких имен интерфейсов при создании нового интерфейса. Как нетрудно заметить, имена нескольких интерфейсов разделяются при этом запятыми.

Report Page