Так как в наследовании участвуют два класса, базовый и производный, не сразу понятно, какой же объект получится в результате. Внешне все выглядит так, словно новый класс имеет тот же интерфейс, что и базовый класс, плюс еще несколько дополнительных методов и полей. Однако наследование не просто копирует интерфейс базового класса. Когда вы создаете объект производного класса, внутри него содержится
Конечно, очень важно, чтобы подобъект базового класса был правильно инициализирован, и гарантировать это можно только одним способом: выполнить инициализацию в конструкторе, вызывая при этом конструктор базового класса, у которого есть необходимые знания и привилегии для проведения инициализации базового класса. Java автоматически вставляет вызовы конструктора базового класса в конструктор производного класса. В следующем примере задействовано три уровня наследования:
//: reusing/Cartoon.java
// Вызовы конструкторов при проведении наследования, import static net.mindview.util.Print.*,
class Art {
ArtO { print("Конструктор Art"); }
}
class Drawing extends Art {
DrawingО { print("Конструктор Drawing"); }
}
public class Cartoon extends Drawing {
public CartoonO { print("Конструктор Cartoon"); } public static void main(String[] args) { Cartoon x = new CartoonO;
}
} /* Output; Конструктор Art Конструктор Drawing Конструктор Cartoon * ///:-
Как видите, конструирование начинается с «самого внутреннего» базового класса, поэтому базовый класс инициализируется еще до того, как он станет доступным для конструктора производного класса. Даже если конструктор класса Cartoon не определен, компилятор сгенерирует конструктор по умолчанию, в котором также вызывается конструктор базового класса.
Конструкторы с аргументами
В предыдущем примере использовались конструкторы по умолчанию, то есть конструкторы без аргументов. У компилятора не возникает проблем с вызовом таких конструкторов, так как вопросов о передаче аргументов не возникает. Если класс не имеет конструктора по умолчанию или вам понадобится вызвать конструктор базового класса с аргументами, этот вызов придется оформить явно, с указанием ключевого слова super и передачей аргументов:
//: reusing/Chess.java
// Наследование, конструкторы и аргументы.
import static net.mindview.util.Print.*;
class Game {
Game(int i) {
print("Конструктор Game"),
}
}
class BoardGame extends Game { BoardGame(int i) { super(i);
print("Конструктор BoardGame");
}
}
public class Chess extends BoardGame { Chess О {
super(ll);
print("Конструктор Chess");
}
public static void main(String[] args) { Chess x = new ChessO:
}
} /* Output-Конструктор Game Конструктор BoardGame Конструктор Chess *///:-
Если не вызвать конструктор базового класса в BoardGame(), то компилятор «пожалуется» на то, что не может обнаружить конструктор в форме Game(). Вдобавок вызов конструктора базового класса должен быть первой командой в конструкторе производного класса. (Если вы вдруг забудете об этом, компилятор вам тут же напомнит.)
Делегирование
Третий вид отношений, не поддерживаемый в Java напрямую, называется
//. reusing/SpaceShipControls.java
public class SpaceShipControls { void up(int velocity) {} void down(int velocity) {} void left(int velocity) {} void right(int velocity) {} void forward(int velocity) {} void back(int velocity) {} void turboBoostO {} } ///-
Для построения космического корабля можно воспользоваться наследованием:
// reusing/SpaceShip java
public class SpaceShip extends S^ceShipControls { private String name.
public SpaceShip(String name) { this.name = name, }
public String toStringO { return name. }__
public static void main(String[] args) {
SpaceShip protector = new SpaceShipC'NSEA Protector"), protector forward(lOO).
}
} /// ~
Однако космический корабль не может рассматриваться как частный случай своего управляющего модуля — несмотря на то, что ему, к примеру, можно приказать двигаться вперед (forward()). Точнее сказать, что SpaceShip
// reusing/SpaceShipDelegation java
public class SpaceShipDelegation { private String name, private SpaceShipControls controls =
new SpaceShipControlsO: public SpaceShipDelegation(String name) {
this name = name. }
// Делегированные методы: public void back(int velocity) { controls.back(velocity);
}
public void down(int velocity) { controls.down(velocity);
}