Java для школьников. Занятие №11. Наследование как метод повторного использования кода

Предыдущее занятие Следущее занятие

На прошлом занятии мы рассмотрели один из вариантов повторного использования кода в Java - композицию. Сегодня мы рассморим другой способ - наследование.

Но сначала ознакомимся с терминами. В словаре у Ожегова читаем:

"НАСЛЕДОВАТЬ, -дую, -дуешь; -анный; сов. и нecoв. 1. что. Получить (-чать) в наследство или в наследие от кого-чего-н. Н. имущество. Н. лучшие традиции...".

В случае программирования в качестве наследства выступает код, а именно - поля и методы класса, который называют базовым или корневым. Соответственно, "класс-наследник" получил название производного класса. В ЯП Java наследование фактически "всегда используется при создании класса, потому что, даже если класс не является производным от другого класса, он наследует от корневого класса Java Object." [1]

Таким образом, наследование, используется в Java фактически везде и играет исключительную роль для понимания как этого языка программирования, так и парадигмы объектно-ориентированного программирования - ООП). При наследовании используется немного другая форма записи класса, в которой фактически говорится: "Новый (производный) класс похож на базовый (или корневой) класс". В программе это записывается с помощью ключевого слова extends (англ., расширяется, распространяется) и последующего имени базового класса (при наследовании от класса Object слово extends для упрощения опускается).

Разберем пример применения наследования, взятый из [2], см.Листинг 1.

Листинг 1. Файл Cartoon.java - пример наследования в Java

package ru.learn2prog.arts;

class Art{
        Art() { System.out.println("Конструктор Art"); }
}
class Drawing extends Art {
                Drawing() { System.out.println("Конструктор Drawing"); }
}
       
public class Cartoon extends Drawing { 
       
        public Cartoon() { System.out.println("Конструктор Cartoon"); }
       
        public static void main(String[] args) {

                Cartoon x = new Cartoon();

        }
}

В этом примере вначале определен базовый класс Art (искусство) а затем с помощью наследования (используем ключевое слово extends) определен производный класс - Drawing (живопись, рисование), в свою очередь, на основании класса Drawing мы создаем класс Cartoon (мультик).

Каждый производный класс имеет свои методы (функции). В данном примере, для простоты, мы применяли в качестве методов только конструкторы. Осталось попробовать скомпилировать и выполнить этот пример. Т.к. в исходном коде нашего примера присутствует директива package, компилировать мы будем с применением пакетов Java. Обратите внимание, что после компиляции нашего примера

javac -d . Cartoon.java

кроме файла Cartoon.class, в директории ru->learn2prog->arts появились файлы, одноименные с остальными классами нашего маленького проекта - Art.class, Drawing.class. Это байт-коды данных классов, которые потребуются виртуальной машине во время выполнения программы. Подробно о файлах .class и компилируемых модулях можно посмотреть здесь.

Теперь выполним нашу программу (в командной строке, конечно):
java ru.learn2prog.arts.Cartoon
Обращаем внимание на вывод программы:
Конструктор Art
Конструктор Drawing
Конструктор Cartoon

Что можно заметить? При создании экземпляра класса Cartoon выполнились конструкторы его классов-предков Art и Drawing. Нужно сказать, что в этой программе при применении наследования проявился, так сказать, "побочный эффект" - эти методы (конструкторы классов Art, Drawing) заработали, хотя их напрямую никто в-общем-то и не вызывал. Эту особенность необходимо учитывать при проектировании системы классов, распределяя код между базовым и производными классами. Однако, продуманная иерархия классов позволяет существенно снизить время разработки, с успехом использовать (как было показано в примере) уже существующий код, и, в добавок, получить возможность правильно инициализировать поля базового класса в том месте, где это наиболее целесообразно (см. предыдущее занятие).

Последнее слово в выборе технологии программирования, т.е. использование наследования или композиции при проектировании системы (или иерархии) классов, всегда остается за программистом. Исключение составляют те случаи, когда используются уже готовые классы или библиотеки и нужно придерживаться тех правил, которые придуманы их авторами. Об этом мы поговорим обязательно позднее.

В заключении отметим, что там же (в [1]) есть весьма оригинальный (с юмором, см. англо-русский словарь) пример наследования в Java-классах, описывающих моющие средства.

1. Брюс Эккель "Философия Java", 4-е издание, с.172
2. Брюс Эккель "Философия Java", 4-е издание, с.175