Чтение онлайн

на главную - закладки

Жанры

Java: руководство для начинающих
Шрифт:

Для того чтобы стало понятнее назначение многоуровневой иерархии, рассмотрим следующий пример программы. В этой программе подкласс Triangle выступает в роли суперкласса для класса ColorTriangle. Класс ColorTriangle наследует все свойства классов Triangle и TwoDShape, а также содержит поле color, определяющее цвет треугольника. // Многоуровневая иерархия, 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; //

Конструктор по умолчанию. 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 * getHeightO / 2; } void showStyle { System.out.println("Triangle is " + style); } } // Подкласс, производный от класса Triangle. // Класс ColorTriangle является подклассом, // производным от класса Triangle, который, в // свою очередь, расширяет класс TwoDShape. // Следовательно, в класс ColorTriangle входят переменные // и методы как из класса Triangle, так из класса TwoDShape. class ColorTriangle extends Triangle { private String color; ColorTriangle(String c, String s, double w, double h) { super(s, w, h); color = c; } String get.Color { return color; } void showColor { System.out.println("Color is " + color); } } class Shapes6 { public static void main(String args[ ]) { ColorTriangle tl = new ColorTriangle("Blue", "right", 8.0, 12.0); ColorTriangle.t2 = new ColorTriangle("Red", "isosceles", 2.0, 2.0); System.out.println("Info for tl: "); tl.showStyle; tl.showDim; tl.showColor; System.out.println ("Area is " + tl.areaO); System.out.println ; System.out.println("Info for t2: "); // Из объекта типа ColorTriangle можно вызывать как его // собственные методы, так и методы его суперклассов. t2.showStyle ; t2.showDim; t2.showColor ; System.out.println("Area is " + t2.area); } }

Результат выполнения данной программы выглядит следующим образом: Info for tl: Triangle is right Width and height are 8.0 and 12.0 Color is Blue Area is 48.0 Info for t2: Triangle is isosceles Width and height are 2.0 and 2.0 Color is Red Area is 2.0

Благодаря наследованию в классе ColorTriangle можно использовать ранее определенные классы Triangle и TwoDShape, дополняя их лишь данными, необходимыми для конкретного применения класса ColorTriangle. Таким образом, наследование способствует повторному использованию кода.

Данный пример демонстрирует еще одну важную деталь: оператор super всегда обращается к конструктору ближайшего суперкласса. Иными словами, оператор super в классе ColorTriangle означает вызов конструктора класса Triangle, а в классе Triangle — вызов конструктора класса TwoDShape. Если в иерархии классов для конструктора суперкласса предусмотрены параметры, то все суперклассы должны передавать их вверх по иерархической структуре. Это правило действует независимого от того, нужны ли параметры самому подклассу или не нужны. Порядок вызова конструкторов

В связи с изложенным выше в отношении наследования и иерархии классов может возникнуть следующий резонный вопрос: когда создается объект подкласса и какой конструктор выполняется первым: тот, что определен в подклассе, или же тот, что определен в суперклассе? Так, если имеется суперкласс А и подкласс В, то вызывается ли конструктор класса А раньше конструктора класса В, или же наоборот? Ответ на этот вопрос состоит в том, что в иерархии классов конструкторы вызываются по порядку выведения классов: от суперкласса к подклассу. Более того, оператор super должен быть первым в конструкторе подкласса, и поэтому порядок, в котором вызываются конструкторы, остается неизменным, независимо от того, используется ли оператор super или нет. Если оператор super отсутствует, то выполняется конструктор каждого суперкласса по умолчанию (т.е. конструктор без параметров). В следующем примере программы демонстрируется порядок вызова конструкторов: // Демонстрация порядка вызова конструкторов. // создать суперкласс class А { А { System.out.println("Constructing A.") ; } } // создать подкласс путем расширения класса А class В extends А { ВО { System.out.println("Constructing В."); } } // создать подкласс путем расширения класса В class С extends В { СО { System.out.println("Constructing С.") ; } } class OrderOfConstruction { public static void main(String args[]) { С с = new С; } }

Ниже приведен результат выполнения данной программы. Constructing А. Constructing В. Constructing С.

Как видите, конструкторы вызываются по порядку выведения их классов.

По зрелом размышлении можно прийти к выводу, что вызов конструкторов по порядку выведения их классов имеет определенный смысл. Ведь суперклассу ничего не известно ни об одном из производных от него подклассов, и поэтому любая инициализация, которая требуется его членам, осуществляется совершенно отдельно от инициализации членов подкласса, а возможно, это и необходимое условие. Следовательно, онадолжна выполняться первой. Ссылки на суперкласс и объекты подклассов

Как вам должно быть уже известно, Java является строго типизированным языком программирования. Помимо стандартных преобразований и автоматического продвижения простых типов данных, в этом языке строго соблюдается принцип совместимости типов. Это означает, что переменная ссылки на объект класса одного типа, как правило, не может ссылаться на объект класса другого типа. В качестве примера рассмотрим следующую простую программу: // Этот код не подлежит компиляции, class X { int а; X(int i) { а = i; } } class Y { int a; Y(int i) { a = i; } } class IncompatibleRef { public static void main(String args[]) { X x = new X(10); X x2; Y

у = new Y(5); x2 = x; // Допустимо, так как обе переменные одного типа. х2 = у; // Ошибка, так как переменные разных типов. } }

Несмотря на то что классы X и Y содержат одинаковые члены, переменной типа X нельзя присвоить ссылку на объект типа Y, поскольку типы объектов отличаются. Вообще говоря, переменная ссылки на объект может указывать только на объекты своего типа.

Но из этого строгого правила соблюдения типов имеется следующее важное исключение: переменной ссылки на объект суперкласса может быть присвоена ссылка на объект любого производного от него подкласса. Следовательно, по ссылке на объект суперкласса можно обращаться к объекту подкласса. Ниже приведен соответствующий пример. // Обращение к объекту подкласса по ссылке на объект суперкласса, class X { int а; X(int i) { а = i; } } class Y extends X { int b; Y(int i, int j) { super(j) ; b = i; } } class SupSubRef { public static void main(String args[]) { X x = new X(10); X x2; Y у = new Y(5, 6) ; x2 = x; // Допустимо, так как обе переменные одного типа. System.out.println("х2.а: " + х2.а); // Класс Y является подклассом X, поэтому переменные х2 и у 1 // могут ссылаться на один и тот же объект производного класса. х2 = у; // По-прежнему допустимо по указанной выше причине. System.out.println("х2.а: " + х2.а); // В классе X известны только члены класса X. х2.а = 19; // Допустимо. //х2.b=27; // Ошибка, так как переменная Ь не является членом класса X. } }

В данном примере класс Y является подклассом X. Следовательно, переменной х2 можно присвоить ссылку на объект типа Y.

Следует особо подчеркнуть, что доступ к конкретным членам класса определяется типом переменной ссылки на объект, а не типом объекта, на который она ссылается. Это означает, что если ссылка на объект подкласса присваивается переменной ссылки на объект суперкласса, то доступ разрешается только к тем частям этого объекта, которые определяются суперклассом. Именно поэтому переменной х2 недоступен член b класса Y, когда она ссылается на объект этого класса. И в этом есть своя логика, поскольку суперклассу ничего не известно о тех членах, которые добавлены в производный от него подкласс. Именно поэтому последняя строка кода в приведенном выше примере была закомментирована.

Несмотря на кажущийся несколько отвлеченным характер приведенных выше рассуждений, им можно найти ряд важных применений на практике. Одно из них рассматривается ниже, а другое — далее в этой главе, когда речь пойдет о переопределении методов.

Один из самых важных моментов для присваивания ссылок на объекты подкласса переменным суперкласса наступает тогда, когда конструкторы вызываются в иерархии классов. Как вам должно быть уже известно, в классе нередко определяется конструктор, принимающий объект своего класса в качестве параметра. Благодаря этому в классе может быть сконструирована копия его объекта. Этой особенностью можно выгодно воспользоваться в подклассах, производных от такого класса. В качестве примера рассмотрим очередные версии классов TwoDShape и Triangle. В оба класса добавлены конструкторы, принимающие объект своего класса в качестве параметра. 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; } // Построение одного объекта на основании другого объекта. TwoDShape(TwoDShape ob) { width = ob.width; height = ob.height; } // Методы доступа к переменным 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 { super; style = "null"; } // Конструктор класса Triangle. Triangle(String s, double w, double h) { super(w, h); // вызвать конструктор суперкласса style = s; } // Конструктор с одним аргументом для построения треугольника. Triangle(double х) { super(х); // вызвать конструктор суперкласса style = "isosceles"; } // построить один объект на основании другого объекта Triangle(Triangle ob) { // Передача ссылки на объект Triangle конструктору класса TwoDShape. super(ob); style = ob.style; } double area { return getWidth * getHeight / 2; } void showStyle { System.out.println("Triangle is " + style); } } class Shapes7 { public static void main(String args[]) { Triangle tl = new Triangle("right", 8.0, 12.0); // создать копию объекта tl Triangle t2 = new Triangle(tl); 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); } }

В приведенном выше примере программы объект t2 конструируется на основании объекта tl, и поэтому они идентичны. Результат выполнения данной программы выглядит следующим образом: 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

Обратите внимание на конструктор класса Triangle, код которого приведен ниже. // построить один объект на основании другого объекта Triangle(Triangle ob) { // Передача ссылки на объект Triangle конструктору класса TwoDShape. super(ob); style = ob.style; }

Поделиться:
Популярные книги

Измена. (Не)любимая жена олигарха

Лаванда Марго
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Измена. (Не)любимая жена олигарха

Хозяйка дома в «Гиблых Пределах»

Нова Юлия
Любовные романы:
любовно-фантастические романы
5.75
рейтинг книги
Хозяйка дома в «Гиблых Пределах»

Фиктивный брак

Завгородняя Анна Александровна
Фантастика:
фэнтези
6.71
рейтинг книги
Фиктивный брак

На границе империй. Том 7. Часть 2

INDIGO
8. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
6.13
рейтинг книги
На границе империй. Том 7. Часть 2

Держать удар

Иванов Дмитрий
11. Девяностые
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Держать удар

Флеш Рояль

Тоцка Тала
Детективы:
триллеры
7.11
рейтинг книги
Флеш Рояль

Драконий подарок

Суббота Светлана
1. Королевская академия Драко
Любовные романы:
любовно-фантастические романы
7.30
рейтинг книги
Драконий подарок

Зауряд-врач

Дроздов Анатолий Федорович
1. Зауряд-врач
Фантастика:
альтернативная история
8.64
рейтинг книги
Зауряд-врач

По дороге на Оюту

Лунёва Мария
Фантастика:
космическая фантастика
8.67
рейтинг книги
По дороге на Оюту

Мастер 5

Чащин Валерий
5. Мастер
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Мастер 5

Не лечи мне мозги, МАГ!

Ордина Ирина
Фантастика:
городское фэнтези
попаданцы
фэнтези
5.00
рейтинг книги
Не лечи мне мозги, МАГ!

Измена. Право на семью

Арская Арина
Любовные романы:
современные любовные романы
5.20
рейтинг книги
Измена. Право на семью

Крепость над бездной

Лисина Александра
4. Гибрид
Фантастика:
боевая фантастика
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Крепость над бездной

Неудержимый. Книга XXI

Боярский Андрей
21. Неудержимый
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Неудержимый. Книга XXI