Java: руководство для начинающих
Шрифт:
Для вызова конструктора суперкласса служит следующая общая форма ключевого слова super: super (список_параметров);
где список_параметров обозначает параметры, необходимые для нормальной работы конструктора суперкласса. Вызов конструктора super должен быть первым оператором в теле конструктора подкласса. Для того чтобы лучше понять особенности вызова super , рассмотрим вариант класса TwoDShape из следующего примера программы, где определен конструктор, инициализирующий переменные экземпляра width и height: // Добавление конструкторов в класс TwoDShape. class TwoDShape { private double width; private double height; // Параметризированный конструктор объектов класса TwoDShape. TwoDShape(double w, double h) { width = w; height = h; } // Методы доступа к переменным экземпляра width и height. double getWidth { return width; } double getHeight { return height; } void setWidth(double w) { width = w; } void setHeight(double h) { height = h; } void showDim { System.out.println("Width and height are " + width + " and " + height); } } // Подкласс, производный от класса TwoDShape, // для представления треугольников, class Triangle extends TwoDShape { private String style; Triangle(String s, double w, double h) { //
В конструкторе Triangle присутствует вызов конструктора super с параметрами w и h. В результате управление получает конструктор TwoDShape , инициализирующий переменные width и height значениями, передаваемыми ему в качестве параметров. Теперь класс Triangle уже не занимается инициализацией элементов суперкласса. Он должен инициализировать только собственную переменную экземпляра style. Конструктору TwoDShape предоставляется возможность построить соответствующий подобъект так, как требуется для данного класса. Более того, в суперклассе TwoDShape можно реализовать функции, о которых не будут знать его подклассы. Благодаря этому код становится более устойчивым к ошибкам.
Любая форма конструктора, определенного в суперклассе, может быть вызвана с помощью оператора super . Для выполнения выбирается тот вариант конструктора, который соответствует указываемым аргументам. В качестве примера ниже приведена расширенная версия классов TwoDShape и Triangle, содержащих конструкторы по умолчанию и конструкторы, принимающие один или более аргумент. // Добавление дополнительных конструкторов в класс TwoDShape. class TwoDShape { private double width; private double height; // Конструктор по умолчанию. TwoDShape { width = height = 0.0; } // Параметризированный конструктор. TwoDShape(double w, double h) { width = w; height = h; } // Конструирование объекта с одинаковыми значениями // переменных экземпляра width и height. TwoDShape(double х) { width = height = x; } // Методы доступа к переменным экземпляра width и height. double getWidth { return width; } double getHeight { return height; } void setWidth(double w) { width = w; } void setHeight(double h) { height = h; } void showDim { System.out.println("Width and height are " + width + " and " + height); } } // Подкласс, производный от класса TwoDShape,. // для представления треугольников, class Triangle extends TwoDShape { private String style; // Использование оператора super для вызова // разных вариантов конструктора TwoDShape . // Конструктор по умолчанию. Triangle { super; // вызвать конструктор суперкласса по умолчанию style = "null"; } // Параметризированный конструктор. Triangle(String s, double w, double h) { super(w, h); // вызвать конструктор суперкласса с двумя аргументами style = s; } // Конструктор с одним аргументом. Triangle(double х) { super(х); // вызвать конструктор суперкласса с одним аргументом style = "isosceles" } double area { return getWidth * getHeight / 2; } void showStyle { System.out.println("Triangle is " + style); } } class Shapes5 { public static void main(String args[]) { Triangle tl = new Triangle; Triangle t2 = new Triangle("right", 8.0, 12.0); Triangle t3 = new Triangle(4.0); tl = t2; System.out.println("Info for tl: "); tl.showStyle; tl.showDim; System.out.println ("Area is " + tl.areaO); System.out.println ; System.out.println("Info for t2: "); t2.showStyle; t2.showDim; System.out.println("Area is " + t2.area); System.out.println; System.out.println("Info for t3: "); t3.showStyle; t3.showDim; System.out.println("Area is " + t3.area); System.out.println; } }
Выполнение этой версии программы дает следующий результат: Info for tl: Triangle is right Width and height are 8.0 and 12.0 Area is 48.0 Info for t2: Triangle is right Width and height are 8.0 and 12.0 Area is 48.0 Info for t3: Triangle is isosceles Width and height are 4.0 and 4.0 Area is 8.0
Еще раз напомним основные свойства вызова конструктора super . Когда этот вызов присутствует в конструкторе подкласса, происходит обращение к конструктору его непосредственного суперкласса. Таким образом, вызывается конструктор того класса, который непосредственно породил вызывающий класс. Это справедливо и при многоуровневой иерархии. Кроме того, вызов конструктора super должен быть первым оператором в теле конструктора подкласса. Применение ключевого слова super для доступа к членам суперкласса
Существует еще одна общая форма ключевого слова super, которая применяется подобно ключевому слову this, но ссылается на суперкласс данного класса. Эта общая форма обращения к члену суперкласса имеет следующий вид: super.член_класса
где член_класса обозначает метод или переменную экземпляра.
Данная форма ключевого слова super применяется в тех случаях, если член подкласса скрывает член суперкласса. Рассмотрим следующий пример несложной иерархии классов: // Применение ключевого слова super для предотвращения сокрытия имен. class А { int i; } // создать подкласс, расширяющий класс А. class В extends А { int i; // Эта переменная i скрывает переменную i из класса А. В (int a, int b) { // Оператор super.i ссылается на переменную i из класса А. super.i = а; // переменная i из класса А i = b; // переменная i из класса В } void show { System, out .println ("i in superclass: 11 + super, i); System.out.println("i in subclass: " + i); } } class UseSuper { public static void main(String args[]) {
Результат выполнения данной программы выглядит следующим образом: i in superclass: 1 i in subclass: 2
Несмотря на то что переменная экземпляра i в классе В скрывает одноименную переменную в классе А, ключевое слово super позволяет обращаться к переменной i из суперкласса. Аналогичным образом ключевое слово super можно использовать для вызова методов суперкласса, скрываемых методами подкласса.
Пример для опробования 7.1. Расширение класса Vehicle
Для того чтобы продемонстрировать возможности наследования, расширим класс Vehicle, созданный в главе 4. Напомним, что класс Vehicle инкапсулирует данные о транспортных средствах и, в частности, сведения о количестве пассажиров, объеме топливного бака и потреблении топлива. Воспользуемся классом Vehicle в качестве заготовки для создания более специализированных классов. Например, транспортным средством, помимо прочих, является грузовик. Одной из важных характеристик грузовика является его грузоподъемность. Поэтому для создания класса Truck можно расширить класс Vehicle, добавив переменную экземпляра, хранящую сведения о допустимом весе перевозимого груза. В этом проекте переменные экземпляра будут объявлены в классе Vehicle как закрытые (private), а для обращения к ним будут созданы специальные методы доступа.
Последовательность действий
Создайте новый файл TruckDemo.java и скопируйте в него исходный код последней версии класса Vehicle, разработанной в главе 4.
Создайте класс Truck, исходный код которого приведен ниже. // Расширение класса Vehicle для грузовиков, class Truck extends Vehicle { private int cargocap; // грузоподъемность в фунтах // Конструктор класса Truck. Truck(int p, int f, int m, int c) { /* Инициализация переменных из класса Vehicle с помощью вызываемого конструктора этого класса. */ super(р, f, m); cargocap = с; } // Методы доступа к переменной cargocap. int getCargo { return cargocap; } void putCargo(int c) { cargocap = c; } }
Здесь класс Truck наследует от класса Vehicle. В класс Truck добавлены новые члены cargocap, getCargo и putCargo . Кроме того, класс Truck содержит все элементы, определенные в классе Vehicle.
Объявите закрытыми переменные экземпляра в классе Vehicle, как показано ниже. private int passengers; // количество пассажиров private int fuelcap; // объем топливного бака в галлонах private int mpg; // потребление топлива в милях на галлон
Ниже приведен весь исходный код программы, в которой демонстрируется класс Truck. // Пример для опробования 7.1. // // Создание подкласса класса Vehicle для грузовиков. class Vehicle { private int passengers; // количество пассажиров private int fuelcap; // объем топливного бака в галлонах private int mpg; // потребление топлива в милях на галлон // Конструктор класса Vehicle. Vehicle(int р, int f, int m) { passengers = p; fuelcap = f; mpg = m; } // возвратить дальность действия транспортного средства int range { return mpg * fuelcap; } // рассчитать объем топлива, требующегося // для прохождения заданного пути double fuelneeded(int miles) { return (double) miles / mpg; } // Методы доступа к переменным экземпляра, int getPassengers { return passengers; } void setPassengers(int p) { passengers = p; } int getFuelcapO { return fuelcap; } void setFuelcap(int f) { fuelcap = f; } int getMpgO { return mpg; } void setMpg(int m) { mpg = m; } } // Расширение класса Vehicle для грузовиков, class Truck extends Vehicle { private int cargocap; // грузоподъемность в фунтах // Конструктор класса Truck. Truck(int p, int f, int m, int c) { /* Инициализация переменных из класса Vehicle с помощью вызываемого конструктора этого класса. */ super(р, f, m); ' cargocap = с; } // Методы доступа к переменной cargocap. int getCargo { return cargocap; } void putCargo(int c) { cargocap = c; } } class TruckDemo { public static void main(String args[]) { // построить ряд новых объектов типа Truck Truck semi = new Truck(2, 200, 7, 44000); Truck pickup = new Truck(3, 28, 15, 2000); double gallons; int dist = 252; gallons = semi.fuelneeded(dist); System.out.println("Semi can carry " + semi.getCargo + " pounds."); System.out.println("To go " + dist + " miles semi needs " + gallons + " gallons of fuel.\n"); gallons = pickup.fuelneeded(dist); System.out.println("Pickup can carry " + pickup.getCargo + " pounds."); System.out.println("To go " + dist + " miles pickup needs " + gallons + " gallons of fuel."); } }
Ниже приведен результат выполнения данной программы. Semi can carry 44000 pounds. То go 252 miles semi needs 36.0 gallons of fuel. Pickup can carry 2000 pounds. To go 252 miles pickup needs 16.8 gallons of fuel.
От класса Vehicle можно породить немало других подклассов. Например, в приведенной нщке заготовке класса, описывающего внедорожники, предусмотрена переменная, содержащая величину дорожного просвета для автомобиля. // Создание класса, описывающего внедорожники, class OffRoad extends Vehicle { private int groundClearance; // дорожный просвет в дюймах // ... }
Итак, имея в своем распоряжении суперкласс, определяющий общие свойства некоторых объектов, можно создать на его основе специализированные подклассы. Каждый подкласс дополняет свойства суперкласса собственными уникальными свойствами. В этом и состоит вся сущность наследования. Создание многоуровневой иерархии классов
В представленных до сих пор примерах программ использовались простые иерархии классов, состоявшие только из суперкласса и подкласса. Но в Java можно также строить иерархии, состоящие из любого числа уровней наследования. Как упоминалось выше, многоуровневая иерархия идеально подходит для использования одного подкласса в качестве суперкласса для другого подкласса. Так, если имеются три класса, А, в и С, то класс С может наследовать от класса В, а тот, в свою очередь, от класса А. В таком случае каждый подкласс наследует характерные особенности всех своих суперклассов. В частности, класс С наследует все члены классов В и А.