1 Композиция, Наследование и Полиморфизм в Java Повторное использование кода - наиболее притягательная возможность языка Java. Это не простое копирование.

Презентация:



Advertisements
Похожие презентации
1 Классы в Java Ключевое слово class означает: Я говорю тебе, как выглядит новый тип объекта. Класс является базовым элементом объектно-ориентированного.
Advertisements

1 Методы Java Методы класса – это подпрограммы, присоединенные к конкретным определениям классов. Они описываются внутри определения класса на том же уровне,
Наследование Наследование – это отношение является между классами. class Person { string first_name; int birth_year;... } class Student : Person { float.
Полиморфизм Полиморфизм (polymorphism) - последний из трех "китов", на которых держится объектно-ориентированное программирование Слово это можно перевести.
Основы информатики Классы Заикин Олег Сергеевич zaikin.all24.org
Полиморфизм. Полиморфизм – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.
1 Java 6. ИНТЕРФЕЙСЫ И ВНУТРЕННИЕ КЛАССЫ. 2 Интерфейсы Не являются классами Ни один из объявленных методов не может быть реализован внутри интерфейса.
ОБЪЕКТНО- ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ (ООП) 1.
Кафедра ОСУ, Java 2004 Слайд 1 Наследование Наследование позволяет использовать существующий класс для определения новых классов, т.е. способствует.
Наследование Полиморфизм ВЫЗОВ КОНСТРУКТОРОВ И ДЕСТРУКТОРОВ ПРИ НАСЛЕДОВАНИИ.
Объектно-ориентированное программирование С++. Лекция 6 Карпов В.Э.
Синтаксис языка Java. Символы и синтаксис Перевод строчки эквивалентен пробелу Регистр в именах различается.
1 Java 6. ИНТЕРФЕЙСЫ И ВНУТРЕННИЕ КЛАССЫ. 2 Интерфейсы Не являются классами Ни один из объявленных методов не может быть реализован внутри интерфейса.
Практическое занятие 6. Функции. Большинство языков программирования используют понятия функции и процедуры. C++ формально не поддерживает понятие процедуры,
1 Контрольное зачетное задание (0, 0)(0, m-1) (n-1, 0)(n-1, m-1) Дано прямоугольное поле, расчерченное на клетки: n клеток в высоту и m клеток в ширину.
Информационные технологии Стандартные библиотечные функции манипулирование данными преобразование и шифрование определение пользователями функций.
Принципы объектно-ориентированного программирования Объектная модель Наследование Инкапсуляция Полиморфизм.
Учебный курс Объектно-ориентированный анализ и программирование Лекция 7 Методы как средство реализации операций Лекции читает кандидат технических наук.
Лекция 4. Введение в С++ Наследование, множественное наследование. Конструкторы, деструкторы. Виртуальные функции.
Лекция 10 ОбъектыЛекция 10 ОбъектыООП Инкапсуляция Возможность совместного хранения данных и кода для их обработки Наследование Возможность расширять существующие.
Транксрипт:

1 Композиция, Наследование и Полиморфизм в Java Повторное использование кода - наиболее притягательная возможность языка Java. Это не простое копирование и изменение свободно распространяемого исходного кода под свои цели. При создании нового класса используются уже существующие классы, которые кто-то уже создал, отладил, описал и распространил. Всегда хочется использовать что-то без копания в исходном коде программ, тем более что это дает реальную возможность не писать все «с нуля». Имеются два способа объектно-ориентированного программирования к повторному использованию кода: Первый - почти прямой: создаются объекты уже существующих классов внутри нового класса. Это называется "композицией", потому, что новый класс создается из объектов уже существующих классов. Повторно используется функциональность кода, но не он сам. Второй подход более искусный: создается новый класс с типом существующего класса. Буквально берется оболочка (интерфейс) существующего класса и добавляется к нему свой код без модификации существующего класса. Этот магический акт называется "наследование", и компилятор языка при этом выполняет большую часть работы. Наследование является одним из краеугольных камней объектно- ориентированного программирования и имеет ключевое значение для реализации эффекта «полиморфизма».

2 Композиция, Наследование и Полиморфизм в Java Композиция и наследование создают новые типы из существующих типов, однако их синтаксис и поведение существенно различаются. Синтаксис композиции class Source { Source( ) { System.out.println(Конструктор Source); } class Target { Source p; Target( ) { System.out.println(Конструктор Target); p = new Source( ); // System.out.println("Значение ссылки на объект Source в конструкторе Target = " + p); } public static void main(String args[ ]){ Target x = new Target( ); // System.out.println("Значение ссылки на объект Target = " + x); } Вывод программы: Конструктор Target Конструктор Source Происходит прямое использование кода класса Source в классе Target. Если код для класса Source находится вне файла для класса Target (в другом файле библиотеки и пакета, присоединенных к классу Target с помощью операторов import и/или package), то также можно говорить о композиции, происходящей опосредованным использованием кода класса Source. Конструктор Target Значение ссылки на объект Source в конструкторе Target = Конструктор Source Значение ссылки на объект Target = Ссылки на объекты p и x инициализируются в null и если попытаться их использовать, то JRE сообщит об ошибке времени выполнения. Единственное, что можно сделать с не инициализированными ссылками на объекты – это их распечатать, например если добавить в исходный код печать значений, занесенных в ссылки p и x, то получим просто адрес регистра памяти, в которой данные ссылки размещены. Инициализировать полностью эти ссылки можно: - В месте, где объект был определен. Это означает, что они будут всегда проинициализированы до того, как будет вызван конструктор. - В конструкторе класса. - Прямо перед использованием этого объекта ("ленивая инициализация»).

3 Композиция, Наследование и Полиморфизм в Java Синтаксис наследования Наследование в Java осуществляется всегда, даже если создаваемый класс явно не объявляется наследником какого либо другого с помощью слова extends. В этом случае происходит неявное наследование от стандартного корневого класса Java Object. Синтаксис наследования похож на композицию, но процедура выполнения заметно отличается. Когда происходит наследование, то это означает: "Этот класс такой же (того же типа), что и класс, имя которого расположено после слова extends" Наследование автоматически дает классу-наследнику доступ ко всем открытым полям и методам базового класса. class Point { int х, у; Point(int х, int у) { this.x = х; this.у = у; } class Point3D extends Point { int z; Point3D(int x, int y, int z) { this.x = x; this.у = у; this.z = z; } } Базовый класс Point Расширяем базовый класс, включением третьей координаты z. Частично повторяется код, уже имевшийся в суперклассе. Ключевое слово super позволяет обратиться непосредственно к конструктору суперкласса и тем самым сократить код программы. Вместо this.x = x; this.у = у; можно записать super(x, y); В классе-наследнике Point3D не понадобилось объявлять переменные х и у, поскольку он унаследовал их от своего суперкласса Point.

4 Композиция, Наследование и Полиморфизм в Java class Cleanser { private String s = new String("Супер-класс Cleanser"); public void append(String a) { s += a; } public void apply() { append(" \nМетод apply()"); } public void scrub() { append(" \nМетод scrub()"); } public void print() { System.out.println(s); } public static void main(String[ ] args) { Cleanser x = new Cleanser(); x.apply(); x.scrub(); x.print(); } public class Detergent extends Cleanser { public void scrub() { append(" \nПерегруженный в дочернем классе "); super.scrub(); // Вызываем метод базового класса } public void foam() { append(" \nМетод дочернего класса foam()"); } public static void main(String[ ] args) { Detergent x = new Detergent(); System.out.println("Тестируем дочерний класс:"); x.apply(); x.scrub(); x.foam(); x.print(); System.out.println("Тестируем базовый класс:"); Cleanser.main(args); } Пример: Detergent.java «Свойства и синтаксис наследования» Специальный вид перегрузки, когда метод базового класса переопределяется в дочернем классе без изменения сигнатуры: ЗАМЕЩЕНИЕ Уникальный метод дочернего класса Запуск класса Detergent Принудительный запуск класса Cleanser

5 Композиция, Наследование и Полиморфизм в Java class Cleanser { private String s = new String("Супер-класс Cleanser"); public void append(String a) { s += a; } public void apply() { append(" \nМетод apply()"); } public void scrub() { append(" \nМетод scrub()"); } public void print() { System.out.println(s); } public static void main(String[ ] args) { Cleanser x = new Cleanser(); x.apply(); x.scrub(); x.print(); } } public class Detergent extends Cleanser { public void scrub() { append(" \nПерегруженный в дочернем классе "); super.scrub(); // Вызываем метод базового класса } public void foam() { append(" \nМетод дочернего класса foam()"); } public static void main(String[ ] args) { Detergent x = new Detergent(); System.out.println("Тестируем дочерний класс:"); x.apply(); x.scrub(); x.foam(); x.print(); System.out.println("Тестируем базовый класс:"); Cleanser.main(args); } } Создание экземпляра x класса- наследника Detergent. Объект х получает доступ ко всем переменным и методам базового класса Cleanser. private переменная s доступна, поскольку классы находятся в одном файле. Вывод программы: Тестируем дочерний класс: Изменение переменной s: Супер-класс Cleanser Метод apply() Перегруженный в дочернем классе Метод scrub() Метод дочернего класса foam() Супер-класс Cleanser Метод apply() Перегруженный в дочернем классе Метод scrub() Метод дочернего класса foam() Тестируем базовый класс: Поскольку метод main – static, его можно вызвать через имя класса Создается экземпляр класса Cleanser. При этом переменная s заново объявляется и инициализируется. Метод scrub() Супер-класс Cleanser Метод apply() Метод scrub()

6 Композиция, Наследование и Полиморфизм в Java Инициализация базового класса Дочерний класс снаружи выглядит, как новый класс имеющий интерфейс базового класса. Наследование не просто копирует интерфейс базового класса. При создании объекта класса- наследника внутри него автоматически создается и содержится подобъект базового класса. Этот подобъект точно такой же, как если бы был создан отдельный экземпляр базового класса. Правильно и корректно проинициализировать этот подобъект можно только через конструктор дочернего класса, путем вызова в нем конструктора базового класса. Ведь только конструктор базового класса имеет все необходимые сведения и привилегии для осуществления инициализации объектов базового класса (в том числе и автоматически сгенерированного в наследуемом классе внутреннего родительского подобъекта). Java автоматически вставляет вызов конструктора базового класса в конструктор наследуемого класса. class Art { Art( ) { System.out.println("Art constructor"); }} class Drawing extends Art { Drawing( ) { System.out.println("Drawing constructor"); }} public class Cartoon extends Drawing { Cartoon( ) { System.out.println("Cartoon constructor"); } public static void main(String[] args) { Cartoon x = new Cartoon( ); }} Вывод этой программы показывает автоматические вызовы: Art constructor Drawing constructor Cartoon constructor Конструкторы базовых классов проинициализировали их встроенные подобъекты до того, как к ним смогли получить доступ классы-наследники Если конструктор для Cartoon( ) не создается, компилятор синтезирует конструктор по умолчанию для последующего вызова из него конструктора базового класса Drawing.

7 Композиция, Наследование и Полиморфизм в Java Конструктор с аргументами В предыдущем примере используются конструкторы по умолчанию(без аргументов). Для компилятора такой вызов прост, нет ненужных вопросов по поводу аргументов, которые нужно передать. Если конструктор базового класса имеет аргументы, автоматического создания конструктора по умолчанию не происходит. Для инициализации в конструкторе класса-нследника необходимо использовать ключевое слово super и передать ему список аргументов: class Game { Game(int i) { System.out.println("Game constructor - " + i); }} class BoardGame extends Game { BoardGame(int i) { super(i++); System.out.println("BoardGame constructor - " + i); }} class Chess extends BoardGame { Chess() { super(11); System.out.println("Chess constructor"); } public static void main(String args[]) { Chess x = new Chess(); }} Если не вызвать конструктор базового класса Game в конструкторе BoardGame( ), тогда компилятор выдаст сообщение, что он не может найти конструктор для Game. В дополнение к вышесказанному - вызов конструктора базового класса должен быть осуществлен в первую очередь в конструкторе класса наследника. (Компилятор сообщит Вам об этом, если Вы сделали что-то не так.) Вывод программы: Game constructor - 11 BoardGame constructor - 12 Chess constructor

8 Композиция, Наследование и Полиморфизм в Java class Pl { Pl(int i) { System.out.println("Pl constructor --> " + i); //i=13 } } class DiPl extends Pl { DiPl(int i) { super(i - 1); System.out.println("DiPl constructor --> " + i); //i=14 } } class Cl { Cl(int i) { System.out.println("Cl constructor --> " + i); //i=10 } } public class DiCl extends Cl { DiPl pl; DiCl(int i) { super(i + 1); pl = new DiPl(i + 5); System.out.println("DiCl constructor --> " + i); //i=9 } public static void main(String args[ ]) { DiCl x = new DiCl(9); } } Вывод программы: Cl constructor 10 Pl constructor 13 DiPl constructor 14 DiCl constructor 9 Наследование: инициализация конструкторов Композиция Наследование: инициализация конструкторов Композиция и наследование могут использоваться в произвольном сочетании

9 Композиция, Наследование и Полиморфизм в Java И композиция, и наследование позволяют поместить подобъект другого класса внутрь нового класса. Какой из способов выбрать? Композиция применяется в основном, когда нужно использовать возможности существующего класса, но не использовать его интерфейс. Объект внедряется так, что можно использовать его для получения доступа к функциональности внедряемого объекта в новом классе. Пользователь нового класса видит интерфейс нового класса раньше, чем интерфейс внедряемого объекта. При наследовании берется существующий класс и создается его специальная версия. В основном это означает, что главный (базовый) класс приспосабливается для частных нужд. Таким образом, при наследовании между классами устанавливается связь «он и есть», при композиции - устанавливается связь «содержит».

10 Композиция, Наследование и Полиморфизм в Java Приведение к базовому типу Наиболее важный аспект наследования заключается вовсе не в снабжении нового класса новыми методами, а в отношении между новым классом и базовым классом. Данное отношение можно определить так "Новый класс имеет тип существующего класса" class Shape { public void canv() { …} static void draw(Shape i) { //... i.canv(); }} class Circle extends Shape{ public static void main(String args [ ]) { Circle cc = new Circle(); Shape.draw(cc); // Upcasting }} Метод draw( ) поддерживает ссылку на Shape. Однако, в Circle.main( ) метод draw( ) вызывается с передачей ссылки на Circle. Метод, принимающий в качестве параметра один тип, вдруг спокойно принимает другой, поскольку объект Circle так же является и объектом типа Shape. Внутри draw( ), код работает с типами Shape и с чем угодно от него произошедшим. Факт конвертации ссылки на Circle в ссылку на Shape называется приведением к базовому типу (upcasting). Базовый класс Shape и дочерний класс Circle. Наследование подразумевает, что все методы базового класса доступны и в дочернем классе. Значит любое сообщение, которое может быть послано базовому классу, так же доступно и в дочернем. Если класс Shape имеет метод canv( ), то и Circle так же может его использовать. Объект типа Circle имеет так же и тип Shape.

11 Композиция, Наследование и Полиморфизм в Java Приведение к базовому типу всегда безопасно, поскольку дочерний класс может содержать больше методов, чем базовый класс, но он должен содержать минимум все те методы, что есть в базовом классе. Только одна вещь может случится при приведении к базовому типу – могут потеряться некоторые (специфические) методы дочернего класса. Резюме. Один из понятных путей для выбора того или иного способа использования готового кода (наследование или композиция) заключается в выяснении: необходимо ли в программе приводить что-то к базовому типу или нет. Если приведение к базовому типу необходимо, то без наследования этого сделать нельзя. Но если же приведение к базовому типу не нужно, то можно обойтись композицией.

12 Композиция, Наследование и Полиморфизм в Java Полиморфизм (динамическое связывание, позднее связывание, связывание во время выполнения). Раннее связывание Полиморфизм Связывание – соединение вызова метода с телом метода.

13 Композиция, Наследование и Полиморфизм в Java Java поддерживает позднее связывание. Следовательно, должен быть механизм определения типа объекта в время работы программы и вызова соответствующего метода. Компилятор Java при трансляции не знает какого типа объект, но механизм вызова методов во время работы программы (полиморфизм) определяет тип объекта и вызывает соответствующее тело метода, который ассоциирован с данным классом. Этот механизм работает одинаково с собственными методами класса, методами, принятыми за счет наследования, а также с методами базового класса, переопределенными «замещенными» в дочернем классе. В любой объект встроена служебная информация о типе этого объекта. Все методы в Java имеют особенности полиморфизма. Если в программе написан доступ к базовому классу, то с этим же кодом будут правильно работать все классы наследники.

14 Композиция, Наследование и Полиморфизм в Java Приведение к базовому типу происходит в выражении: Shape s = new Circle(); Создается объект Circle и результирующей ссылке s немедленно присваивается тип Shape. Ошибка (присвоение одного типа другому) не происходит, так как Circle есть Shape через наследование. Предположим, что в программе происходит вызов метода draw(), который имеется в базовом классе и переопределен («замещен») в дочернем классе: s.draw(); Базовый класс Shape и множество дочерних типов: Circle, Square, Triangle и т.д. Какой метод будет вызван: Shape.draw( ) или Circle.draw( ) ??? Поскольку используется позднее связывание(полиморфизм), будет вызван Circle.draw( ).

15 Композиция, Наследование и Полиморфизм в Java class Shape { void draw( ) { } } class Circle extends Shape { void draw( ) { System.out.println("Circle.draw()"); }} class Square extends Shape { void draw( ) { System.out.println("Square.draw()"); }} class Triangle extends Shape { void draw( ) { System.out.println("Triangle.draw()"); }} public class Shapes { public static Shape randShape( ) { switch((int)(Math.random( ) * 3)) { default: case 0: return new Circle( ); case 1: return new Square( ); case 2: return new Triangle( ); } } public static void main(String args [ ]) { Shape[] s = new Shape[9]; for(int i = 0; i < s.length; i++) { s[i] = randShape(); s[i].draw( ); } }} Базовый класс Shape c методом draw() Классы-наследники Circle, Square, Triangle с переопределенными («замещенными» внутри методами draw() Запускающий класс Shapes. Метод randShape() случайным образом генерирует объекты классов-наследников и возвращает через приведение к базовому типу объекты типа Shape. Запуск программы. В цикле генерируются 10 объектов Shape и заносятся в массив s. После этого вызывается полиморфный метод draw() каждый раз со своим объектом. Вывод программы будет каждый раз разный, но ни разу не будет вызван метод базового класса Shape.draw(): Circle.draw() Triangle.draw() Circle.draw() Square.draw() Triangle.draw() Square.draw()

16 Композиция, Наследование и Полиморфизм в Java