Эккель Брюс
Шрифт:
print(this + ".playO " + n),
}
public String toStringO { return "Percussion"; } public void adjustO { print(this + " adjustO"); }
}
class Stringed implements Instrument { public void play(Note n) {
print(this + ".playO " + n);
}
public String toStringO { return "Stringed"; } public void adjustO { print(this + ".adjustO"); }
}
class Brass extends Wind {
public String toStringO { return "Brass"; }
}
class Woodwind extends Wind {
public String toStringO { return "Woodwind"; }
}
public class Music5 {
// Работа метода не зависит от фактического типа объекта. // поэтому типы, добавленные в систему, будут работать правильно: static void tune(Instrument i) { // .
i.play(Note.MIDDLE_C);
}
static void tuneAll(Instruments e) { for(Instrument i : e) tune(i);
}
public static void main(String[] args) {
// Восходящее преобразование при добавлении в массив. Instrument!!] orchestra = { new WindO. new PercussionO. new StringedO, new BrassO. new WoodwindО
}.
tuneAll(orchestra),
}
} /* Output: Wind.pi ayО MIDDLE_C Percussion.playO MIDDLE_C Stringed.pi ayО MIDDLE_C Brass.pi ayО MIDDLE_C Woodwind pi ayО MIDDLE_C */// ~
В этой версии присутствует еще одно изменение: метод what был заменен на toString. Так как метод toString входит в корневой класс Object, его присутствие в интерфейсе не обязательно.
Остальной код работает так же, как прежде. Неважно, проводите ли вы преобразование к «обычному» классу с именем Instrument, к абстрактному классу с именем Instrument или к интерфейсу с именем Instrument — действие будет одинаковым. В методе tune ничто не указывает на то, является класс Instrument «обычным» или абстрактным, или это вообще не класс, а интерфейс.
Отделение интерфейса от реализации
В любой ситуации, когда метод работает с классом вместо интерфейса, вы ограничены использованием этого класса или его субклассов. Если метод должен быть применен к классу, не входящему в эту иерархию, — значит, вам не повезло. Интерфейсы в значительной мере ослабляют это ограничение. В результате код становится более универсальным, пригодным для повторного использования.
Представьте, что у нас имеется класс Processor с методами name и process. Последний получает входные данные, изменяет их и выдает результат. Базовый класс расширяется для создания разных специализированных типов Processor. В следующем примере производные типы изменяют объекты String (обратите внимание: ковариантными могут быть возвращаемые значения, но не типы аргументов):
//• interfaces/classprocessor/Apply.java package interfaces classprocessor; import java.util.*;
import static net.mindview.util.Print.*;
class Processor {
public String nameО {
return getClass.getSimpleName;
}
Object process(Object input) { return input; }
class Upcase extends Processor {
String process(Object input) { // Ковариантный возвращаемый тип return ((String)input) toUpperCase,
class Downcase extends Processor { String process(Object input) {
return ((String)input) toLowerCase,
class Splitter extends Processor { String process(Object input) {
// Аргумент splitO используется для разбиения строки return Arrays toString(((String)input) splitC ")),
public class Apply {
public static void process(Processor p. Object s) { print ("Используем Processor " + p nameO), print(p.process(s));
}
public static String s =
"Disagreement with beliefs is by definition incorrect"; public static void main(String[] args) { process(new UpcaseO, s); process(new Downcase, s); process(new SplitterO, s),
}
} /* Output:
Используем Processor Upcase
DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT Используем Processor Downcase disagreement with beliefs is by definition incorrect Используем Processor Splitter
[Disagreement, with, beliefs, is, by, definition, incorrect] *///-
Метод Apply.process получает любую разновидность Processor, применяет ее к Object, а затем выводит результат. Метод split является частью класса String. Он получает объект String, разбивает его на несколько фрагментов по ограничителям, определяемым переданным аргументом, и возвращает String[]. Здесь он используется как более компактный способ создания массива String.
Теперь предположим, что вы обнаружили некое семейство электронных фильтров, которые тоже было бы уместно использовать с методом Apply. process:
// interfaces/filters/Waveform java package interfaces.filters.
public class Waveform {
private static long counter;
private final long id = counter++; public String toStringO { return "Waveform " + id. } } Hill- interfaces/filters/Filter java package interfaces filters,
public class Filter {