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

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

Жанры

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; }

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

Его наследник

Безрукова Елена
1. Наследники Сильных
Любовные романы:
современные любовные романы
эро литература
5.87
рейтинг книги
Его наследник

Сердце Дракона. Том 9

Клеванский Кирилл Сергеевич
9. Сердце дракона
Фантастика:
фэнтези
героическая фантастика
боевая фантастика
7.69
рейтинг книги
Сердце Дракона. Том 9

Белые погоны

Лисина Александра
3. Гибрид
Фантастика:
фэнтези
попаданцы
технофэнтези
аниме
5.00
рейтинг книги
Белые погоны

Системный Нуб 4

Тактарин Ринат
4. Ловец душ
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Системный Нуб 4

Барон меняет правила

Ренгач Евгений
2. Закон сильного
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Барон меняет правила

(Не)нужная жена дракона

Углицкая Алина
5. Хроники Драконьей империи
Любовные романы:
любовно-фантастические романы
6.89
рейтинг книги
(Не)нужная жена дракона

Я Гордый часть 2

Машуков Тимур
2. Стальные яйца
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я Гордый часть 2

На границе империй. Том 5

INDIGO
5. Фортуна дама переменчивая
Фантастика:
боевая фантастика
попаданцы
7.50
рейтинг книги
На границе империй. Том 5

Сотник

Ланцов Михаил Алексеевич
4. Помещик
Фантастика:
альтернативная история
5.00
рейтинг книги
Сотник

Мятежник

Прокофьев Роман Юрьевич
4. Стеллар
Фантастика:
боевая фантастика
7.39
рейтинг книги
Мятежник

Не грози Дубровскому! Том II

Панарин Антон
2. РОС: Не грози Дубровскому!
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Не грози Дубровскому! Том II

Огни Эйнара. Долгожданная

Макушева Магда
1. Эйнар
Любовные романы:
любовно-фантастические романы
эро литература
5.00
рейтинг книги
Огни Эйнара. Долгожданная

Жена по ошибке

Ардова Алиса
Любовные романы:
любовно-фантастические романы
7.71
рейтинг книги
Жена по ошибке

Черный Маг Императора 9

Герда Александр
9. Черный маг императора
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Черный Маг Императора 9