Существует два основных механизма для построения новых классов из существующих: наследование и агрегация.
Имеет смысл наследовать от существующего класса Vehicle
(транспортное средство), чтобы описать класс Car
(автомобиль), поскольку автомобиль является транспортным средством. Класс Vehicle
имеет несколько частей; поэтому имеет смысл определить композитный объект класса Vehicle
, состоящий из объектов-компонентов таких классов, как Motor
(двигатель), Axle
(вал) и GearBox
(коробка передач), которые составляют транспортное средство.
Наследование демонстрируется на примере, реализующем стек символов, который может отобразить свои элементы на терминал. Этот новый стек имеет все свойства и поведения класса CharStack
, но он также имеет дополнительную способность выводить свои элементы на экран. Задано, что этот распечатываемый стек является стеком символов, поэтому он может быть выведен из класса CharStack
. Эти отношения показаны на рис. 1.6.
Класс PrintableCharStack
называется подклассом, класс CharStack
называется суперклассом. Класс CharStack
служит обобщением (generalization) для всех стеков символов, тогда как класс PrintableCharStack
является специализацией (specialization) стеков символов, потому что может также отображать на экран свои элементы.
Рис. 1.6. Диаграмма класса, изображающая отношение наследования
В Java в описании нового класса, получаемого на основе существующего класса, требуется применение оператора extends
. Подкласс может расширять только один суперкласс. Подкласс наследует члены суперкласса. Следующий фрагмент кода реализует класс PrintableCharStack
.
// Имя исходного файла: PrintableCharStack.java
class PrintableCharStack extends CharStack { // (1)
// Метод экземпляра
public void printStackElements() { // (2)
// Реализация метода ...
}
// Конструктор явно вызывает конструктор суперкласса
public PrintableCharStack(int capacity) {
super(capacity); // (3)
}
}
Класс PrintableCharStack
расширяет класс CharStack
в строке (1). В реализации метода printStackElements()
в классе PrintableCharStack
необходимо обратиться к полю stackArray
суперкласса CharStack
. Но это поле private
и поэтому недоступно в подклассе. Подкласс сможет получить доступ к этим полям, если их доступность изменить на protected
в классе CharStack
.
В примере 1.3 используется версия класса CharStack
, которая соответствующим образом была изменена. Реализация метода printStackElements()
показана в строке (2). Конструктор класса PrintableCharStack
в строке (3) вызывает конструктор суперкласса CharStack
для того, чтобы правильно проинициализировать стек.
Пример 1.3. Определение подкласса
// Имя исходного файла: CharStack.java
public class CharStack {
// Переменные экземпляра теперь доступны подклассам
protected char[] stackArray;
protected int topOfStack;
// Остальные определения оставляем без изменений.
}
// Имя исходного файла: PrintableCharStack.java
class PrintableCharStack extends CharStack { // (1)
// Метод экземпляра
public void printStackElements() {
// Реализация метода (2)
for (char c : stackArray)
System.out.print(c); // Выводим каждый символ на экран
System.out.println();
}
// Конструктор явно вызывает конструктор суперкласса
public PrintableCharStack(int capacity) {
super(capacity); // (3)
}
}
Объекты класса PrintableCharStack
ведут себя так же, как объекты класса CharStack
, но они имеют также дополнительную функциональность, определенную в подклассе.
PrintableCharStack aPrintableCharStack = new PrintableCharStack(3);
aPrintableCharStack.push('H');
aPrintableCharStack.push('i');
aPrintableCharStack.push('!');
aPrintableCharStack.printStackElements(); // Будет выведено "Нi!"
Терминология для членов класса | Агрегация |