
- •Конструкторы классов
- •Наследование
- •Модификаторы видимости
- •Перегрузка
- •Полиморфизм
- •Приемы программирования: наследование и полиморфизм
- •Конструктор по умолчанию
- •Вызов конструктора суперкласса
- •Приведение типов
- •Оператор instanceof
- •Анонимные и вложенные классы
- •Модификатор static
- •Модификатор final
- •Абстрактные классы
- •Множественное наследование
- •Описание интерфейса
- •Реализация интерфейса
- •Переменные интерфейсного типа
- •Приемы программирования: пример применения интерфейсов
- •Пакеты и области видимости Пакеты
- •Импортирование пакетов
- •Файловая структура Java-проекта
- •Области видимости классов
- •Области видимости членов класса
- •Области видимости переменных
- •Конфликты имен
- •Ход работы:
- •Задание №1
- •Задание №2
- •Задание №2
- •Задание № 3
- •Контрольные вопросы:
Конструктор по умолчанию
Если в классе не описан ни один конструктор, для него автоматически создается конструктор по умолчанию. Этот конструктор не имеет параметров, все, что он делает – это вызывает конструктор без параметров класса-предка.
Поэтому мы и смогли создать объект класса BigDog в примере с большой собакой, хотя не описывали в классе никаких конструкторов. Если вспомнить конструктор без параметров, который у нас есть в классе Dog, мы поймем, что переменная bigdog в предыдущем примере ссылалась на собаку по кличке "Незнакомец".
Вызов конструктора суперкласса
В примере с большой собакой нам удалось создать ее с помощью конструктора без параметров, т. е. не указывая ее имя и возраст. Оказывается, если бы мы попытались сделать это по-другому, у нас бы не получилось. Дело в том, что конструкторы не считаются членами класса и, в отличие от других методов, не наследуются.
BigDog bigdog = new BigDog("Полкан", 8); // Ошибка. Такого конструктора в классе нет
Для того, чтобы мы могли создавать больших собак с интересующими нас именем и возрастом, необходимо написать подходящий конструктор. При этом не обязательно повторять те команды, которые мы писали в конструкторе класса Dog (их всего две, но ведь могло быть гораздо больше). Вместо этого мы можем написать:
BigDog (String n, int a) {
super(n, a);
}
Ключевое слово super означает суперкласс (в нашем случае это класс Dog). В примере мы вызываем с его помощью конструктор суперкласса. При этом мы передаем два параметра – строку и число, – так что из всех конструкторов будет выбран именно тот, который нас интересует.
Вызов конструктора суперкласса должен происходить в самом начале конструктора.
Вместо вызова конструктора суперкласса можно вызвать один из конструкторов того же самого класса. Это делается с помощью ключевого слова this() – с параметрами в скобках, если они нужны.
Если в начале конструктора нет ни вызова this(), ни вызова super(), автоматически происходит обращение к конструктору суперкласса без аргументов.
Приведение типов
Объект класса-потомка можно присвоить переменной типа класса-предка. При этом Java производит автоматическое преобразование типа, называемое расширением. Расширение – это переход от более конкретного типа к менее конкретному. Переход от byte к int – это тоже расширение.
Рассмотрим пример. В программе есть класс User, предназначенный для обработки информации о пользователях системы. В этом классе есть метод enter(String login, String password), который возвращает true, если переданные в метод логин и пароль совпадают с логином и паролем, скрытым в полях класса.
Мы наследуем от класса User два подкласса: Admin и, к примеру, Member (для программы координации участников встречи). Класс Admin может понадобиться нам впоследствии для каких-то специфичных действий, связанных с управлением системой, а класс Member моделирует участников проекта, которые с помощью программы пытаются выбрать оптимальное место для встречи. Открытый метод addRequest(String place, int day, int from, int to) вызывается, когда участник проекта предлагает новый вариант времени и места встречи.
В главном классе программы мы храним массив users, содержащий всех пользователей системы. Элементы этого массива имеют тип User, но мы можем присваивать им ссылки на объекты как класса Member, так и класса Admin. В этот момент и будет происходить расширение типа.
Member member = new Member(...);
users[3] = member; // Java проводит автоматическое преобразование типа Member к типу User, чтобы поместить переменную member в массив users
Для того, чтобы найти пользователя с введенными логином и паролем программа выполняет запрос:
for (int i = 0; i < users.length; i++) {
if (users[i].enter(log, passw)) currentUser = users[i];
}
Несмотря на то, что все объекты, добавленные в массив, сохраняют свой «настоящий» класс, программа работает с ними как с объектами класса User. Этого вполне достаточно, чтобы можно было найти нужного пользователя по логину и паролю (ведь метод enter() у них общий) и присвоить найденный объект переменной currentUser типа User. В этой переменной хранится текущий пользователь, авторизовавшийся в системе.
Предположим, нам известно, что переменная currentUser сейчас ссылается на объект класса Member и текущий пользователь предлагает встретиться у фонтана в среду с 17 до 19 часов. Необходимо вызвать метод addRequest(), но у нас не получится сделать это командой
currentUser.addRequest("Фонтан", 3, 17, 19);
поскольку в классе User нет метода addRequest().
Однако мы можем осуществить явное преобразование переменной currentUser к типу Member. Такое преобразование (переход от менее конкретного типа к более конкретному) называется сужением. Явное преобразование делается с помощью оператора, представляющего собой имя целевого типа в скобках.
((Member)currentUser).addRequest("Фонтан", 3, 17, 19);
Здесь мы, прежде чем вызвать метод addRequest(), преобразовали переменную currentUser к типу Member. Нам было позволено сделать это, поскольку Member является потомком User. Однако, если бы во время выполнения программы оказалось, что на самом деле переменная currentUser не ссылалась на объект класса Member, в программе возникла бы ошибка.