25. Java – Полиморфизм
Полиморфизм – способность объекта принимать множество различных форм. Наиболее распространенное использование полиморфизма в ООП происходит, когда ссылка на родительский класс используется для ссылки на объект дочернего класса. Постараемся разобраться с понятием полиморфизма в Java простыми словами, так сказать для чайников.
Любой объект в Java, который может пройти более одного теста IS-A считается полиморфным. В Java все объекты полиморфны, так как любой объект пройдёт тест IS-A для своего собственного типа и для класса Object.
Важно знать, что получить доступ к объекту можно только через ссылочную переменную. Ссылочная переменная может быть только одного типа. Будучи объявленной, тип ссылочной переменной изменить нельзя.
Ссылочную переменную можно переназначить к другим объектам, которые не объявлены как final. Тип ссылочной переменной определяет методы, которые она может вызвать на объекте.
Ссылочная переменная может обратиться к любому объекту своего объявленного типа или любому подтипу своего объявленного типа. Ссылочную переменную можно объявить как класс или тип интерфейса.
Пример 1
Рассмотрим пример наследования полиморфизм в Java.
public interface Vegetarian<> public class Animal<> public class Deer extends Animal implements Vegetarian<>
Теперь класс Deer (Олень) считается полиморфным, так как он имеет множественное наследование. Следующие утверждения верны для примера выше:
- A Deer IS-A Animal (олень — это животное);
- A Deer IS-A Vegetarian (олень — это вегетарианец);
- A Deer IS-A Deer (олень — это олень);
- A Deer IS-A Object (олень — это объект).
Когда мы применяем факты ссылочной переменной к ссылке на объект Deer (Олень), следующие утверждения верны:
Пример 2
Deer d = new Deer(); Animal a = d; Vegetarian v = d; Object o = d;
Все переменные (d, a, v, o) ссылаются к тому же объекту Deer (Олень).
Виртуальные методы
В этом разделе рассмотрим, как поведение переопределённых методов в Java позволяет воспользоваться преимуществами полиморфизма при оформлении классов.
Мы уже рассмотрели переопредение методов, где дочерний класс может переопределить метод своего «родителя». Переопределённый метод же скрыт в родительском классе и не вызван, пока дочерний класс не использует ключевое слово super во время переопределения метода.
Пример
/* File name : Employee.java */ public class Employee < private String name; private String address; private int number; public Employee(String name, String address, int number) < System.out.println("Собираем данные о работнике"); this.name = name; this.address = address; this.number = number; >public void mailCheck() < System.out.println("Отправляем чек " + this.name + " " + this.address); >public String toString() < return name + " " + address + " " + number; >public String getName() < return name; >public String getAddress() < return address; >public void setAddress(String newAddress) < address = newAddress; >public int getNumber() < return number; >>
Теперь предположим, что мы наследуем класс Employee следующим образом:
/* File name : Salary.java */ public class Salary extends Employee < private double salary; // Годовая заработная плата public Salary(String name, String address, int number, double salary) < super(name, address, number); setSalary(salary); >public void mailCheck() < System.out.println("Внутри mailCheck класса Salary "); System.out.println("Отправляем чек " + getName() + " с зарплатой " + salary); >public double getSalary() < return salary; >public void setSalary(double newSalary) < if(newSalary >= 0.0) < salary = newSalary; >> public double computePay() < System.out.println("Вычисляем заработную плату для " + getName()); return salary/52; >>
Теперь, внимательно изучите программу и попытайтесь предугадать её вывод:
/* File name : VirtualDemo.java */ public class VirtualDemo < public static void main(String [] args) < Salary s = new Salary("Олег Петров", "Минск, Беларусь", 3, 3600.00); Employee e = new Salary("Иван Иванов", "Москва, Россия", 2, 2400.00); System.out.println("Вызываем mailCheck, используя ссылку Salary --"); s.mailCheck(); System.out.println("Вызываем mailCheck, используя ссылку Employee --"); e.mailCheck(); >>
После запуска программы будет выдан такой результат:
Собираем данные о работнике Собираем данные о работнике Вызываем mailCheck, используя ссылку Salary –– Внутри mailCheck класса Salary Отправляем чек Олег Петров с зарплатой 3600.0 Вызываем mailCheck, используя ссылку Employee –– Внутри mailCheck класса Salary Отправляем чек Иван Иванов с зарплатой 2400.0
Итак, мы создали два объекта Salary. Один использует ссылку Salary, то есть s, а другой использует ссылку Employee, то есть e.
Во время вызова s.mailCheck(), компилятор видит mailCheck() в классе Salary во время компиляции, а JVM вызывает mailCheck() в классе Salary при запуске программы.
mailCheck() в e совсем другое, потому что e является ссылкой Employee. Когда компилятор видит e.mailCheck(), компилятор видит метод mailCheck() в классе Employee.
Во время компиляции был использован mailCheck() в Employee, чтобы проверить это утверждение. Однако во время запуска программы JVM вызывает mailCheck() в классе Salary.
Это поведение называется вызовом виртуальных методов, а эти методы называются виртуальными. Переопределённый метод вызывается во время запуска программы вне зависимости от того, какой тип данных был использован в исходном коде во время компиляции.
Оглавление
- 1. Java – Самоучитель для начинающих
- 2. Java – Обзор языка
- 3. Java – Установка и настройка
- 4. Java – Синтаксис
- 5. Java – Классы и объекты
- 6. Java – Конструкторы
- 7. Java – Типы данных и литералы
- 8. Java – Типы переменных
- 9. Java – Модификаторы
- 10. Java – Операторы
- 11. Java – Циклы и операторы цикла
- 11.1. Java – Цикл while
- 11.2. Java – Цикл for
- 11.3. Java – Улучшенный цикл for
- 11.4. Java – Цикл do..while
- 11.5. Java – Оператор break
- 11.6. Java – Оператор continue
- 12. Java – Операторы принятия решений
- 12.1. Java – Оператор if
- 12.2. Java – Оператор if..else
- 12.3. Java – Вложенный оператор if
- 12.4. Java – Оператор switch..case
- 12.5. Java – Условный оператор (? 🙂
- 13. Java – Числа
- 13.1. Java – Методы byteValue(), shortValue(), intValue(), longValue(), floatValue(), doubleValue()
- 13.2. Java – Метод compareTo()
- 13.3. Java – Метод equals()
- 13.4. Java – Метод valueOf()
- 13.5. Java – Метод toString()
- 13.6. Java – Метод parseInt()
- 13.7. Java – Метод Math.abs()
- 13.8. Java – Метод Math.ceil()
- 13.9. Java – Метод Math.floor()
- 13.10. Java – Метод Math.rint()
- 13.11. Java – Метод Math.round()
- 13.12. Java – Метод Math.min()
- 13.13. Java – Метод Math.max()
- 13.14. Java – Метод Math.exp()
- 13.15. Java – Метод Math.log()
- 13.16. Java – Метод Math.pow()
- 13.17. Java – Метод Math.sqrt()
- 13.18. Java – Метод Math.sin()
- 13.19. Java – Метод Math.cos()
- 13.20. Java – Метод Math.tan()
- 13.21. Java – Метод Math.asin()
- 13.22. Java – Метод Math.acos()
- 13.23. Java – Метод Math.atan()
- 13.24. Java – Метод Math.atan2()
- 13.25. Java – Метод Math.toDegrees()
- 13.26. Java – Метод Math.toRadians()
- 13.27. Java – Метод Math.random()
- 14. Java – Символы
- 14.1. Java – Метод Character.isLetter()
- 14.2. Java – Метод Character.isDigit()
- 14.3. Java – Метод Character.isWhitespace()
- 14.4. Java – Метод Character.isUpperCase()
- 14.5. Java – Метод Character.isLowerCase()
- 14.6. Java – Метод Character.toUpperCase()
- 14.7. Java – Метод Character.toLowerCase()
- 14.8. Java – Метод Character.toString()
- 15. Java – Строки
- 15.1. Java – Метод charAt()
- 15.2. Java – Метод compareTo()
- 15.3. Java – Метод compareToIgnoreCase()
- 15.4. Java – Метод concat()
- 15.5. Java – Метод contentEquals()
- 15.6. Java – Метод copyValueOf()
- 15.7. Java – Метод endsWith()
- 15.8. Java – Метод equals()
- 15.9. Java – Метод equalsIgnoreCase()
- 15.10. Java – Метод getBytes()
- 15.11. Java – Метод getChars()
- 15.12. Java – Метод hashCode()
- 15.13. Java – Метод indexOf()
- 15.14. Java – Метод intern()
- 15.15. Java – Метод lastIndexOf()
- 15.16. Java – Метод length()
- 15.17. Java – Метод matches()
- 15.18. Java – Метод regionMatches()
- 15.19. Java – Метод replace()
- 15.20. Java – Метод replaceAll()
- 15.21. Java – Метод replaceFirst()
- 15.22. Java – Метод split()
- 15.23. Java – Метод startsWith()
- 15.24. Java – Метод subSequence()
- 15.25. Java – Метод substring()
- 15.26. Java – Метод toCharArray()
- 15.27. Java – Метод toLowerCase()
- 15.28. Java – Метод toString()
- 15.29. Java – Метод toUpperCase()
- 15.30. Java – Метод trim()
- 15.31. Java – Метод valueOf()
- 15.32. Java – Классы StringBuilder и StringBuffer
- 15.32.1. Java – Метод append()
- 15.32.2. Java – Метод reverse()
- 15.32.3. Java – Метод delete()
- 15.32.4. Java – Метод insert()
- 15.32.5. Java – Метод replace()
- 16. Java – Массивы
- 17. Java – Дата и время
- 18. Java – Регулярные выражения
- 19. Java – Методы
- 20. Java – Потоки ввода/вывода, файлы и каталоги
- 20.1. Java – Класс ByteArrayInputStream
- 20.2. Java – Класс DataInputStream
- 20.3. Java – Класс ByteArrayOutputStream
- 20.4. Java – Класс DataOutputStream
- 20.5. Java – Класс File
- 20.6. Java – Класс FileReader
- 20.7. Java – Класс FileWriter
- 21. Java – Исключения
- 21.1. Java – Встроенные исключения
- 22. Java – Вложенные и внутренние классы
- 23. Java – Наследование
- 24. Java – Переопределение
- 25. Java – Полиморфизм
- 26. Java – Абстракция
- 27. Java – Инкапсуляция
- 28. Java – Интерфейсы
- 29. Java – Пакеты
- 30. Java – Структуры данных
- 30.1. Java – Интерфейс Enumeration
- 30.2. Java – Класс BitSet
- 30.3. Java – Класс Vector
- 30.4. Java – Класс Stack
- 30.5. Java – Класс Dictionary
- 30.6. Java – Класс Hashtable
- 30.7. Java – Класс Properties
- 31. Java – Коллекции
- 31.1. Java – Интерфейс Collection
- 31.2. Java – Интерфейс List
- 31.3. Java – Интерфейс Set
- 31.4. Java – Интерфейс SortedSet
- 31.5. Java – Интерфейс Map
- 31.6. Java – Интерфейс Map.Entry
- 31.7. Java – Интерфейс SortedMap
- 31.8. Java – Класс LinkedList
- 31.9. Java – Класс ArrayList
- 31.10. Java – Класс HashSet
- 31.11. Java – Класс LinkedHashSet
- 31.12. Java – Класс TreeSet
- 31.13. Java – Класс HashMap
- 31.14. Java – Класс TreeMap
- 31.15. Java – Класс WeakHashMap
- 31.16. Java – Класс LinkedHashMap
- 31.17. Java – Класс IdentityHashMap
- 31.18. Java – Алгоритмы Collection
- 31.19. Java – Iterator и ListIterator
- 31.20. Java – Comparator
- 32. Java – Дженерики
- 33. Java – Сериализация
- 34. Java – Сеть
- 34.1. Java – Обработка URL
- 35. Java – Отправка Email
- 36. Java – Многопоточность
- 36.1. Java – Синхронизация потоков
- 36.2. Java – Межпоточная связь
- 36.3. Java – Взаимная блокировка потоков
- 36.4. Java – Управление потоками
- 37. Java – Основы работы с апплетами
- 38. Java – Javadoc
Полиморфизм
Полиморфизм (polymorphism) — это понятие из объектно-ориентированного программирования, которое позволяет разным сущностям выполнять одни и те же действия. При этом неважно, как эти сущности устроены внутри и чем они различаются.
С греческого языка слово «полиморфизм» означает «многообразие». Термин используют и в других сферах. Скажем, полиморфизм в биологии — способность какого-то вида существовать в разных формах.
Пример полиформности в природе — пчелы: есть рабочие пчелы, матка, трутни. Они разные, но все могут летать независимо от того, что это за пчела. По похожему принципу работает и полиморфизм в ООП.

Освойте профессию
«Fullstack-разработчик на Python»
Профессия / 12 месяцев
Fullstack-разработчик на Python
Создавайте веб-проекты самостоятельно

Fullstack-разработчик на Python
Fullstack-разработчики могут в одиночку сделать IT-проект от архитектуры до интерфейса. Их навыки востребованы у работодателей, особенно в стартапах. Научитесь программировать на Python и JavaScript и создавайте сервисы с нуля.

Например, есть две разных сущности: картинка и видео. И тем, и другим можно поделиться: отправить в личное сообщение другому человеку. Программист может сделать два разных метода — один для картинки, другой для видео. А может воспользоваться полиморфизмом и создать один метод «Отправить» для обеих сущностей.
Такой метод будет называться полиморфным. Плюс этого подхода — разработчик пишет меньше кода и не повторяется.
Чтобы лучше понять, что такое полиморфизм и как он работает, советуем прочитать статью про ООП. Полиморфизм — один из четырех основных принципов этого способа программирования
Для чего нужен полиморфизм
- Облегчает написание кода. Не нужно придумывать десять разных методов: отправить одно, другое, третье. Есть один метод, который можно применять к разным сущностям и не задумываться.
- Позволяет масштабировать решения. Если понадобится отправлять не только видео и картинки, но и текст, это можно будет сделать той же командой.
- Делает код более читаемым. Разработчику не нужно разбираться, что делает десяток методов с похожими названиями. Есть один метод, и по его названию все понятно.
- Помогает точно понимать, чего ждать от разных методов, то есть делает код более предсказуемым. Не может быть такого, что метод «Отправить» вдруг окажется методом не для картинки, а для видео.
С понятием должен быть знаком любой разработчик. Множество языков программирования используют полиморфизм: C, C++, Java, Python и другие. Не все эти языки чисто объектно-ориентированные: некоторые устроены так, что с ними можно работать в разных парадигмах программирования. Так что столкнуться с полиморфизмом может каждый.

Станьте Fullstack-разработчик на Python и найдите стабильную работу
на удаленке
Полиморфизм как принцип ООП
В объектно-ориентированном программировании четыре основных принципа: инкапсуляция, абстракция, наследование и полиморфизм. Это связанные между собой вещи — без одного не работало бы и другое.
Инкапсуляция — это создание сущностей как «вещей в себе». Классы должны работать независимо друг от друга: если какая-то сущность, например, удалится, это не повлияет на принцип работы остальных.
Абстракция — это принцип, когда какие-то общие вещи сводятся до набора абстрактных признаков. То есть мы имеем не абсолютно разные классы «картинка», «видео», «текст», а абстрактный класс «контент».
Наследование — это возможность делать на основе одних сущностей другие. Обычно в качестве «родителя» используются абстрактные сущности, а от них наследуются уже более конкретные. То есть если родитель — «контент», то дети — «картинка», «видео», «текст». Это все подвиды контента и его наследники.
Чтобы реализовать полиморфизм, нужны как минимум абстракция и наследование. Они помогают сделать абстрактный класс, в нем — абстрактный «общий» метод, а потом унаследовать разные реализации этого метода. В итоге название одно, а механика разная в зависимости от подтипа. Сейчас разберемся подробнее.
Что такое полиморфный метод и как его создают
Полиформный — это многообразный: формы различаются, а действие одно и то же. Тот же самый метод «Отправить» из примера выше может быть реализован по-разному для видео и картинки. Но вызывается он одинаково для всех видов контента и выполняет одну и ту же задачу. Вот как это работает.
Сначала программист создает общий класс. Например, «контент». В нем он описывает вещи, общие для всего контента: свойства и методы. Свойства — это признаки, то, что есть у контента: количество лайков, возможность комментирования и так далее. А методы — это действия, то есть команды: контент можно лайкнуть, открыть на отдельной вкладке, репостнуть или отправить в личное сообщение.
У общего класса — абстрактные, общие методы. Контент можно отправить, но как — пока непонятно. Зато уже можно описать, как будет выглядеть эта команда: как она называется, что в нее нужно передать, какое сообщение выдать после этого. Это своего рода каркас будущего конкретного действия.
Затем разработчик создает производные классы. Это наследники общего класса: они более конкретные, у них могут быть дополнительные свойства и методы. Например, видео можно поставить на паузу, а картинку — нет. А еще у них сохраняются абстрактные свойства и методы родителя: их переопределяют, чтобы они тоже работали конкретнее.
У производных классов — свои реализации общих методов. Например, в классе «контент» есть метод «отправить». Он же есть и в производных классах. В классе «картинка» может быть свой код для отправки, а в классе «видео» — свой. Они могут быть похожи или различаться. Но название у них окажется одинаковым, и делать они будут одно и то же.
Можно создавать объекты производных классов и пользоваться их методами. Абстрактные классы существуют как бы в вакууме: нельзя создать объект, который будет принадлежать такому классу. Среди реальных объектов не может быть «просто контента», который не является ни текстом, ни картинкой, ни видео, ни еще чем-то. Соответственно, абстрактные методы никто не будет вызывать. А вот переопределенные методы из производных классов — вполне реальные, ими можно пользоваться в разных ситуациях.
Формы полиморфизма
Существуют разные виды полиморфизма. Вообще-то классификация довольно широкая и начинающему легко в ней запутаться, поэтому мы решили ограничиться только основными формами.
Полиморфизм подтипов. Это полиморфность «по умолчанию»: когда в ООП говорят о полиморфизме, обычно имеют в виду его. Выше мы рассказывали именно про такой тип. Это возможность использовать одни и те же команды, или интерфейсы, для разных сущностей — подтипов.
Параметрический полиморфизм. Его еще называют обобщенным полиморфизмом. В нем для команды не имеет значения, какую сущность ей прислали: для всех возможных классов будет использоваться один код. Такой полиморфизм считается «истинным» и делает код универсальнее, но реализовать его сложнее.
Полиморфизм ad hoc. Этот вид полиморфизма еще называют специализированным. Его иногда противопоставляют параметрическому: идея ad hoc — разный код при одинаковом названии. Часто такой полиморфизм реализуют с помощью перегрузки методов: несколько раз пишут метод с одним и тем же названием, но разным кодом.
Статический и динамический полиморфизм
На более «глубоком», близком к машине уровне полиморфизм можно разделить на две группы — статический и динамический. Разница —в том, когда программа переходит от общего метода к одной из его вариаций.
- Статический — метод переопределяют при компиляции.
- Динамический — при выполнении программы.
Статический полиморфизм реализуют с помощью перегрузки методов, о которой мы рассказывали выше. Динамический — с помощью абстракций. Обычно в объектно-ориентированных языках есть возможность применить оба варианта.
Преимущества полиморфизма
- Код становится аккуратнее: не приходится множить сущности и создавать десяток команд, которые делают одно и то же. Нет «лапши» — бессвязного неструктурированного кода, в котором тяжело разобраться.
- Разработчику удобнее: не нужно каждый раз думать, что делает команда «Отправить» для конкретного вида контента, можно просто написать ее и получить результат.
- Работать с разными сущностями можно одинаково: не обязательно каждый раз узнавать, о каком именно производном классе речь идет на этот раз. Ведь общее полиморфное действие есть для всего.
- Код легче расширять, использовать заново и всячески модифицировать.
Недостатки полиморфизма
- Связь полиморфизма с наследованием порой расценивают как слабое место всей концепции. Если нужен полиформный класс, но для конкретной ситуации не подходит наследование, — реализация может усложниться.
- Не всегда полиморфизм легко реализовать на практике. Поэтому существует довольно много реализаций, которые работают плохо: с багами и необъяснимыми ошибками.
- Полиморфизм может ухудшать производительность кода, делать его более «тяжелым» и медленным. Но тут многое зависит от реализации: скажем, параметрический обычно быстрее, чем ad hoc.
- Новичкам бывает тяжело понять принцип — объяснить полиморфизм простыми словами можно только в связке с другими понятиями ООП. Так что человек на момент изучения уже должен понимать, что такое объектно-ориентированное программирование.
Как начать изучать полиморфизм
Мы советуем начать с основ: сначала разобраться, как работает программирование в целом, потом перейти к принципам ООП и полиморфизму. На всех этапах лучше практиковаться, чтобы закреплять знания, — к тому же на реальных примерах легче понять тот или иной концепт.
Вы можете записаться на наши курсы и начать учиться уже сейчас. Обещаем много реальных задач и интересной практики!
Что такое полиморфизм в java
Полиморфизм на первый взгляд кажется самой малоинтересной и малоперспективной парадигмой, но на самом деле это совсем не так. Полиморфизм удивительно мощная и востребованная парадигма. Давайте попробуем разобраться, что это такое.
Полиморфизмом назвается возможность работать с несколькими типами так, как будто это один и тот же тип и в то же время поведение каждого типа будет уникальным в зависимости от его реализации. Возможно, что вы ничего не поняли, поэтому попробую описать это иначе и на примере. Давайте так и сделаем.
В предыдущей части мы создавали класс RobotTotal, который наследовался от класса Robot. Если немного подумать, то по парадигме наследования будет интуитивно понятно, что класс RobotTotal является пусть и несколько измененным, но тем не менее классом Robot. Исходя из этого вполне непротиворечивого соображения мы можем написать несколько иную реализацию класса RobotManager
package edu . javacourse . robot ;
public class RobotManager
public static void main ( String [ ] args ) <
// Первое проявление полиморфизма - ссылке на класс-предок
// можно присвоить класс-потомок
Robot robot = new RobotTotal ( 0 , 0 ) ;
robot . forward ( 20 ) ;
robot . setCourse ( 90 ) ;
robot . forward ( 20 ) ;
robot . setCourse ( 90 ) ;
robot . forward ( 50 ) ;
// Напечатать координаты
robot . printCoordinates ( ) ;
// Напечатать общую дистанцию уже не получится
// компилятор выдает ошибку
//System.out.println(robot.getTotalDistance());
Как видим, создавая объект RobotTotal мы его «сужаем» до объекта класса Robot. С одной стороны это выглядит достаточно логично и непротиворечиво — RobotTotal является роботом. С другой стороны возникает вопрос — а метод forward будет вызываться какой ? Класса RobotTotal или Robot ? Думаю, что для вас ответ «Как RobotTotal» выглядит предпочтительнее — и это соврешенно правильный ответ. Можете в этом убедиться сами, добавив в методв forward какую-либо поясняющую печать. Например так:
public void forward ( int distance ) < super . forward ( distance ) ; totalDistance += distance ; System . out . println ( "RobotTotal" ) ;
В этом можно убедиться еще более удобным и практичным образом — спросить у объекта его класс и у класса спросить его имя. Этот механизм называется Reflection — он позволяет получить информацию об объекте прямо в момент выполнения программы. Мы будем его рассматривать несколько позже. Вот как может выглядеть такой вызов:
Расшифровывается это приблизительно так: сначала у объекта robot получаем его класс методом getClass(). Возвращается объект типа Class (есть такой стандартный класс — Class). И у этого класса есть метод, который возращает имя — getName(). Вот возможный полный код:
package edu . javacourse . robot ;
public class RobotManager
public static void main ( String [ ] args ) <
Robot robot = new RobotTotal ( 0 , 0 ) ;
System . out . println ( robot . getClass ( ) . getName ( ) ) ;
Как видим мы можем присвоить ссылке на объект класса-предка объект-потомок — и это работает. С одной стороны, мы работаем как-будто с классом Robot, с другой стороны — поведение нашего обхекта соответствует классу RobotTotal. ВАЖНО . А вот в обратную сторону присваивание НЕ работает. На запись вот такого вида
RobotTotal robot = new Robot(0, 0);
компилятор будет выдавать ошибку.
Думаю, что с технической точки зрения все достаточно понятно. Но возникает логичный вопрос — зачем это вообще надо ? С инкапсуляцией более-менее понятно, с наследованием — в принципе тоже. Но вот этот механизм зачем, какое преимущество мы получим при использовании этой парадигмы ? Для первого приближения рассмотрим наш пример графического приложения, в котором мы создавали свой компонент OvalComponent. Мы использовали модифицированный класс JFrame (OvalFrame) и что весьма важно, мы использовали уже готовый метод add для добавления нашего объекта на форму. Я бы хотел заострить ваше внимание на этом весьма тонком моменте — мы использовали УЖЕ существующий метод существующего класса JFrame. И этот метод (да и класс тоже) совершенно не осведомлен о нашем новом классе OvalComponent. И тем не менее он прекрасно с ним работает — мы сами это видели. Думаю, что вы уже догадались в чем фокус, но я тем не менее проговорю эту мысль — класс JFrame умеет работать с классами-потомками от класса JComponent и ему не важно, какой точно класс он получил — они для него все являются объектами класса JComponent. И это здорово нам помогает. Т.к. наш класс OvalComponent рисует себя сам путем вызова метода paintComponent где-то у себя, то ситуация еще более восхитительна — вызывается именно НАШ метод paintComponent. Значит мы можем написать много разных классов унаследованных от класса JComponent, положить их на форму и все они будут рисоваться так, как они сами это умеют.
Что еще интересно — для самого себя класс тоже может вести себя полиморфно. Если внимательно посмотреть на код класса OvalComponent метод paintComponent объявлен как protected и вызывается внутри класса JComponent и никак иначе. Снаружи другим классам он недоступен. Т.е. все наследники класса JComponent предоставляют свои реализации метода paintComponent и вызывают его внутри унаследованного метода paint, который уже объявлен как public. Иными словами — все наследники используют метод paintComponent из уже готового метода paint. Возможно, вы еще не совсем готовы оценить «красоту игры», но на мой взгляд полиморфизм является просто чудесной штукой. Мы еще вернемся к этой весьма увлекательной парадигме, ну а пока сделаем еще одно графическое приложение, которое позволит нам поместить разные типы компонентов на форму, о которых она не знает, но тем не менее сможет прекрасно ими управлять.
Графическое приложение
Данное приложение несколько сложнее предыдущего — здесь создаются разные компоненты для рисования разных фигур. Приложение содержит 5 классов: 3 класса являются компонентами, которые рисуют внутри себя три разных фигуры (овал, треугольник и прямоугольник), класс для отображения окна и класс для создания и отображения самой формы. Сначала рассмотрим три класса для рисования фигур. Они используют методы класса Graphics и вряд ли требуют каких-либо комментариев. Я их поместил в отдельный пакет.
Что такое полиморфизм в Java
Привет! Это статья об одном из принципов ООП — полиморфизм.
Что такое полиморфизм
Определение полиморфизма звучит устрашающе
Полиморфизм — это возможность применения одноименных методов с одинаковыми или различными наборами параметров в одном классе или в группе классов, связанных отношением наследования.
Слово «полиморфизм» может показаться сложным — но это не так. Нужно просто разбить данное определение на части и показать на примерах, что имеется в виду. Поверьте, уже в конце статьи данное определение полиморфизма покажется Вам понятным
Полиморфизм, если перевести, — это значит «много форм». Например, актер в театре может примерять на себя много ролей — или принимать «много форм».

Так же и наш код — благодаря полиморфизму он становится более гибким, чем в языках программирования, которые не используют принципы ООП.
Так о каких формах идет речь? Давайте сначала приведем примеры и покажем, как на практике проявляется полиморфизм, а потом снова вернемся к его определению.
Как проявляется полиморфизм
Дело в том, что если бы в Java не было принципа полиморфизма, компилятор бы интерпретировал это как ошибку:

Как видите, методы на картинке отличаются значениями, которые они принимают:
Однако, поскольку в Java используется принцип полиморфизма, компилятор не будет воспринимать это как ошибку, потому что такие методы будут считаться разными:

Называть методы одинаково — это очень удобно. Например, если у нас есть метод, который ищет корень квадратный из числа, гораздо легче запомнить одно название (например, sqrt()), чем по одному отдельному названию на этот же метод, написанный для каждого типа:

Как видите, мы не должны придумывать отдельное название для каждого метода — а главное их запоминать! Очень удобно.
Теперь Вы можете понять, почему часто этот принцип описывают фразой:
Один интерфейс — много методов
Это предполагает, что мы можем заполнить одно название (один интерфейс), по которому мы сможем обращаться к нескольким методам.
Перегрузка методов
То, что мы показывали выше — несколько методов с одним названием и разными параметрами — называется перегрузкой. Но это был пример перегрузки метода в одном классе. Но бывает еще один случай — переопределение методов родительского класса.
Переопределение методов родителя
Когда мы наследуем какой-либо класс, мы наследуем и все его методы. Но если нам хочется изменить какой-либо из методов, который мы наследуем, мы можем всего-навсего переопределить его. Мы не обязаны, например, создавать отдельный метод с похожим названием для наших нужд, а унаследованный метод будет «мертвым грузом» лежать в нашем классе.
Именно то, что мы можем создать в классе-наследнике класс с таким же названием, как и класс, который мы унаследовали от родителя, и называется переопределением.
Пример
Представим, что у нас есть такая структура:
Вверху иерархии классов стоит класс Animal. Его наследуют три класса — Cat, Dog и Cow.
У класса «Animal» есть метод «голос» (voice). Этот метод выводит на экран сообщение «Голос». Естественно, ни собака, ни кошка не говорят «Голос» Они гавкают и мяукают. Соответственно, Вам нужно задать другой метод для классов Cat, Dog и Cow — чтобы кошка мяукала, собака гавкала, а корова говорила «Муу».
Поэтому, в классах-наследниках мы переопределяем метод voice(), чтобы мы в консоли получали «Мяу», «Гав» и «Муу».
- Обратите внимание: перед методом, который мы переопределяем, пишем «@Override«. Это дает понять компилятору, что мы хотим переопределить метод.
Так что же такое полиморфизм
Тем не менее, полиморфизм — это принцип. Все реальные примеры, которые мы приведодили выше — это только способы реализации полиморфизма.
Давайте снова посмотрим на определение, которое мы давали в начале статьи:
Полиморфизм — возможность применения одноименных методов с одинаковыми или различными наборами параметров в одном классе или в группе классов, связанных отношением наследования.
Выглядит понятнее, правда? Мы показали, как можно:
- создавать «одноименные методы» в одном классе («перегрузка методов»)
- или изменить поведение методов родительского класса («переопределение методов»).
Все это — проявления «повышенной гибкости» объектно-ориентированных языков благодаря полиморфизму.
Надеемся, наша статья была Вам полезна. Записаться на наши курсы по Java можно у нас на сайте.
- ← Перечисления Enum в Java — Часть 1
- Map в Java. Hashmap в Java →
