Чтение онлайн

на главную - закладки

Жанры

Философия Java3

Эккель Брюс

Шрифт:

Давайте возьмем пример с объектами Instrument и включим дополнительные методы в базовый класс, а также определим несколько новых классов. Рассмотрим диаграмму (см. рисунок на обороте).

Все новые классы правильно работают со старым, неизмененным методом tune. Даже если метод tune находится в другом файле, а к классу Instrument присоединяются новые методы, он все равно будет работать верно без повторной компиляции. Ниже приведена реализация рассмотренной диаграммы:

//. polymorph"!sm/music3/Music3.java // Расширяемая программа package polymorphism music3; import polymorphism.music Note; import static net.mindview.util.Print *;

продолжение &

class Instrument {

void play(Note л) { print("Instrument playO " + n). }

String what О { return "Instrument". }

void adjustO { printC'Adjusting Instrument"). }

}

class Wind extends Instrument {

void play(Note n) { print ("Wind playO " + n), }

String whatO { return "Wind"; }

void adjustO { printC'Adjusting Wind"). }

}

class Percussion extends Instrument {

void play(Note n) { printC'Percussion.playO " + n). }

String whatO { return "Percussion"; }

void adjustO { printC'Adjusting Percussion"), }

}

class Stringed extends Instrument {

void play(Note n) { printC'Stringed playO " + n), }

String whatO { return "Stringed". }

void adjustO { printC'Adjusting Stringed"); }

}

class Brass extends Wind {

void play(Note n) { print("Brass.play " + n); } void adjustO { printC'Adjusting Brass"); }

}

class Woodwind extends Wind {

void play(Note n) { print ("Woodwind playO " + n); } String whatO { return "Woodwind"; }

public class Music3 {

// Работа метода не зависит от фактического типа объекта, // поэтому типы, добавленные в систему, будут работать правильно public static void tune(Instrument i) { // ...

i.play(Note.MIDDLE_C),

}

public static void tuneAll(Instrument!!] 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.playO MIDDLE_C Woodwind pi ayО MIDDLE_C *///:-

Новый метод what возвращает строку (String) с информацией о классе, а метод adjust предназначен для настройки инструментов.

В методе main сохранение любого объекта в массиве orchestra автоматически приводит к выполнению восходящего преобразования к типу Instrument.

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

Проблема: «переопределение» закрытых методов

Перед вами одна из ошибок, совершаемых по наивности:

//: polymorph!sm/PrivateOverride.java

// Попытка переопределения приватного метода

package polymorphism;

import static net.mindview.util.Print.*;

public class PrivateOverride {

private void f { printCprivate f(D; } public static void main(String[] args) {

Pri vateOverride po = new DerivedO; po.fO:

}

class Derived extends PrivateOverride {

public void f { print("public f"). } } /* Output

private f *///-

Вполне естественно было бы ожидать, что программа выведет сообщение public f, но закрытый (private) метод автоматически является неизменным (final), а заодно и скрытым от производного класса. Так что метод f класса Derived в нашем случае является полностью новым — он даже не был перегружен, так как метод f базового класса классу Derived недоступен.

Из этого можно сделать вывод, что переопределяются только методы, не являющиеся закрытыми. Будьте внимательны: компилятор в подобных ситуациях не выдает сообщений об ошибке, но и не делает того, что вы от него ожидаете. Иными словами, методам производного класса следует присваивать имена, отличные от имен закрытых методов базового класса.

Конструкторы и полиморфизм

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

Порядок вызова конструкторов

Порядок вызова конструкторов коротко обсуждался в главах 5 и 7, но в то время мы еще не рассматривали полиморфизм.

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

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

// polymorphism/Sandwich.java

// Порядок вызова конструкторов.

package polymorphism,

import static net mindview.util.Print.*;

class Meal {

Meal О { printCMealO"). }

}

class Bread {

BreadO { printCBreadO"). }

}

class Cheese {

CheeseO { printC'CheeseO"). }

}

class Lettuce {

LettuceO { print("Lettuce"); }

Поделиться:
Популярные книги

Камень

Минин Станислав
1. Камень
Фантастика:
боевая фантастика
6.80
рейтинг книги
Камень

Ваантан

Кораблев Родион
10. Другая сторона
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Ваантан

Локки 10. Потомок бога

Решетов Евгений Валерьевич
10. Локки
Фантастика:
фэнтези
юмористическое фэнтези
героическая фантастика
боевая фантастика
5.00
рейтинг книги
Локки 10. Потомок бога

Сфирот

Прокофьев Роман Юрьевич
8. Стеллар
Фантастика:
боевая фантастика
рпг
6.92
рейтинг книги
Сфирот

Шайтан Иван 2

Тен Эдуард
2. Шайтан Иван
Фантастика:
боевая фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Шайтан Иван 2

Двойник Короля 4

Скабер Артемий
4. Двойник Короля
Фантастика:
аниме
фэнтези
фантастика: прочее
попаданцы
5.00
рейтинг книги
Двойник Короля 4

На границе империй. Том 10. Часть 8

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 8

Треск штанов

Ланцов Михаил Алексеевич
6. Сын Петра
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Треск штанов

Имя нам Легион. Том 8

Дорничев Дмитрий
8. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Имя нам Легион. Том 8

Деревенщина в Пекине

Афанасьев Семён
1. Пекин
Фантастика:
попаданцы
дорама
фантастика: прочее
5.00
рейтинг книги
Деревенщина в Пекине

Старый, но крепкий 4

Крынов Макс
4. Культивация без насилия
Фантастика:
уся
фэнтези
5.00
рейтинг книги
Старый, но крепкий 4

Долг

Кораблев Родион
7. Другая сторона
Фантастика:
боевая фантастика
5.56
рейтинг книги
Долг

Сирийский рубеж

Дорин Михаил
5. Рубеж
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Сирийский рубеж

Твое сердце будет разбито. Книга 1

Джейн Анна
Любовные романы:
современные любовные романы
5.50
рейтинг книги
Твое сердце будет разбито. Книга 1