Полиморфизм java что это
Полиморфизм в Java – подробно с примерами
Полиморфизм в Java – это концепция ООП, в которой одно имя может иметь много форм.
Например, у вас есть смартфон для общения. Режим связи, который вы выбираете, может быть любым. Это может быть звонок, текстовое сообщение, графическое сообщение, почта и т. д. Итак, общая цель – общение, но у них другой подход. Это называется полиморфизмом.
Объяснение полиморфизма
У нас есть один родительский класс, «Счет» с функцией пополнения и снятия. Учетная запись имеет 2 дочерних класса.
Операции ввода и вывода одинаковы для Сберегательного и Чекового счетов. Таким образом, унаследованные методы из класса Account будут работать.
Изменение требований к программному обеспечению
В спецификации требований произошли изменения, что так часто встречается в индустрии программного обеспечения. Вы должны добавить функциональность привилегированного банковского счета с помощью овердрафта.
Овердрафт – это средство, с помощью которого вы можете снять сумму, превышающую доступный остаток на вашем счете.
Итак, метод вывода для привилегированных нужд должен быть реализован заново. Но вы не меняете проверенный кусок кода в Сберегательном и Чековом счете. Это преимущество ООП.
Шаг 1) Такой, что при вызове «изъятого» метода для сохранения учетной записи выполняется метод из класса родительской учетной записи.
Шаг 2) Но когда вызывается метод «Снять» для привилегированной учетной записи (средство овердрафта), выполняется метод вывода, определенный в привилегированном классе. Это полиморфизм.
Переопределение метода
Переопределение метода – переопределение метода суперкласса в подклассе.
Правила для переопределения метода
Разница между перегрузкой и переопределением
Перегрузка метода находится в одном классе, где несколько методов имеют одно и то же имя, но разные подписи.
Переопределение метода – это когда один из методов суперкласса переопределяется в подклассе. В этом случае подпись метода остается неизменной.
Что такое динамический полиморфизм?
Динамический полиморфизм – это механизм, с помощью которого можно определить несколько методов с одинаковыми именами и сигнатурами в суперклассе и подклассе. Вызов переопределенного метода разрешается во время выполнения.
Пример динамического полиморфизма
Ссылочная переменная суперкласса может ссылаться на объект подкласса.
Здесь ссылочная переменная “obj” относится к родительскому классу, но объект, на который она указывает, принадлежит к дочернему классу (как показано на диаграмме).
obj.treatPatient() выполнит метод TreatPatient() для подкласса – Surgeon
Если для вызова метода используется ссылка на базовый класс, то вызываемый метод определяется JVM в зависимости от объекта, на который указывает ссылка
Например, хотя obj является ссылкой на Doctor, он вызывает метод Surgeon, так как он указывает на объект Surgeon.
Это определяется во время выполнения и, следовательно, называется динамическим или динамическим полиморфизмом.
super ключевое слово
Что, если метод TreatPatient в классе Surgeon хочет выполнить функциональность, определенную в классе Doctor, а затем выполнить свою собственную специфическую функциональность? В этом случае ключевое слово super может использоваться для доступа к методам родительского класса из дочернего класса. Метод TreatPatient в классе Surgeon может быть записан как:
Ключевое слово super может использоваться для доступа к любому члену данных или методам суперкласса в подклассе.
Шаг 1) Скопируйте следующий код в редактор
Шаг 2) Сохранение, компиляция Запустите код. Соблюдайте вывод.
Шаг 3) Раскомментируйте строки № 6-9. Сохранить, скомпилировать Запустите код. Соблюдайте вывод.
Шаг 4) Раскомментируйте строку # 10. Сохраните и Скомпилируйте код.
Шаг 5) Ошибка =? Это потому, что подкласс не может получить доступ к закрытым членам суперкласса.
Разница между статическим и динамическим полиморфизмом
Ошибки, если таковые имеются, устраняются во время компиляции. Поскольку код не выполняется во время компиляции, отсюда и название static.
В случае, если ссылочная переменная вызывает переопределенный метод, вызываемый метод определяется объектом, на который указывает ваша ссылочная переменная. Это может быть определено только во время выполнения, когда код находится в процессе выполнения, поэтому имя динамическое.
Java Challengers #3: Полиморфизм и наследование
Мы продолжаем перевод серии статей с задачками по Java. Прошлый пост про строки вызвал на удивление бурную дискуссию. Надеемся, что мимо этой статьи вы тоже не пройдете мимо. И да — мы приглашаем теперь на юбилейный десятый поток нашего курса «Разработчик Java».
Согласно легендарному Венкату Субраманиам (Venkat Subramaniam) полиморфизм является самым важным понятием в объектно — ориентированном программировании. Полиморфизм — или способность объекта выполнять специализированные действия на основе его типа — это то, что делает Java — код гибким. Шаблоны проектирования, такие как Команда (Command), Наблюдатель (Observer), Декоратор (Decorator), Стратегия (Strategy), и многие другие, созданные бандой четырех (Gang Of Four), все используют ту или иную форму полиморфизма. Освоение этой концепции значительно улучшит вашу способность продумывать программные решения.
Вы можете взять исходный код для этой статьи и поэксперементировать здесь: https://github.com/rafadelnero/javaworld-challengers
Интерфейсы и наследование в полиморфизме
В этой статье мы сфокусируемся на связи между полиморфизмом и наследованием. Главное иметь в виду, что полиморфизм требует наследования или реализации интерфейса. Вы можете увидеть это на примере ниже с Дюком ( Duke ) и Джагги ( Juggy ):
Вывод этого кода будет таким:
Перегрузка (overloading) метода — это полиморфизм? Многие программисты путают отношение полиморфизма с переопределением методов (overriding) и перегрузкой методов (overloading). Фактически, только переопределение метода — это истинный полиморфизм. Перегрузка использует то же имя метода, но разные параметры. Полиморфизм — это широкий термин, поэтому всегда будут дискуссии на эту тему.
Какова цель полиморфизма
Большим преимуществом и целью использования полиморфизма является уменьшение связанности клиентского класса с реализацией. Вместо того чтобы хардкодить, клиентский класс получает реализацию зависимости для выполнения необходимого действия. Таким образом, клиентский класс знает минимум для выполнения своих действий, что является примером слабого связывания.
Чтобы лучше понять цель полиморфизма, взгляните на SweetCreator :
Аннотация @Override обязывает программиста использовать такую же сигнатуру метода, которая должна быть переопределена. Если метод не переопределен, будет ошибка компиляции.
Ковариантные возвращаемые типы при переопределении метода
Можно изменить тип возвращаемого значения переопределенного метода если это ковариантный тип. Ковариантный тип в основном является подклассом возвращаемого значения.
Полиморфизм в базовых классах Java
Рассмотрим пример кода, использующий Java Collections API без полиморфизма:
Отвратительный код, не так ли? Представьте себе, что вам нужно его сопровождать! Теперь рассмотрим тот же пример с полиморфизмом:
Вызов конкретных методов для полиморфного метода
Можно вызвать конкретные методы при полиморфном вызове метода, это происходит за счет гибкости. Вот пример:
Техника, которую мы используем здесь — это приведение типов (casting) или сознательное изменение типа объекта во время выполнения.
Обратите внимание, что вызов определенного метода возможен только при приведении более общего типа к более специфичному типу. Хорошей аналогией было бы сказать явно компилятору: «Эй, я знаю, что я здесь делаю, поэтому я собираюсь привести объект к определенному типу и буду использовать этот метод.»
Ключевое слово instanceof
Ключевое слово super
Решите задачку по полиморфизму
Давайте проверим, что вы узнали о полиморфизме и наследовании.
В этой задачке Вам дается несколько методов от Matt Groening’s The Simpsons, от вавам требуется разгадать, какой будет вывод для каждого класса. Для начала внимательно проанализируйте следующий код:
Как вы думаете? Каким будет результат? Не используйте IDE, чтобы выяснить это! Цель в том, чтобы улучшить ваши навыки анализа кода, поэтому постарайтесь решить самостоятельно.
Выберите ваш ответ (правильный ответ вы сможете найти в конце статьи).
A)
I love Sax!
D’oh
Simpson!
D’oh
B)
Sax 🙂
Eat my shorts!
I love Sax!
D’oh
Knock Homer down
C)
Sax 🙂
D’oh
Simpson!
Knock Homer down
D)
I love Sax!
Eat my shorts!
Simpson!
D’oh
Knock Homer down
Что случилось? Понимание полиморфизма
Для следующего вызова метода:
вывод будет «I love Sax!». Это потому, что мы передаём строку в метод и у класса Lisa есть такой метод.
Для следующего вызова:
Теперь смотрите, это немного сложнее:
В этом случае на выходе будет «Simpson!».
Распространенные ошибки с полиморфизмом
Распространенная ошибка думать, что можно вызвать конкретный метод без использования приведения типа.
Другой ошибкой является неуверенность в том, какой метод будет вызван при полиморфном создании экземпляра класса. Помните, что вызываемый метод является методом созданного экземпляра.
Также помните, что переопределение метода не является перегрузкой метода.
Невозможно переопределить метод, если параметры отличаются. Можно изменить тип возвращаемого значения переопределенного метода, если возвращаемый тип является подклассом.
Что нужно помнить о полиморфизме
Созданный экземпляр определяет, какой метод будет вызван при использовании полиморфизма.
Аннотация @Override обязывает программиста использовать переопределенный метод; в противном случае возникнет ошибка компилятора.
Полиморфизм может использоваться с обычными классами, абстрактными классами и интерфейсами.
Большинство шаблонов проектирования зависят от той или иной формы полиморфизма.
Единственный способ вызвать нужный ваш метод в полиморфном подклассе — это использовать приведение типов.
Можно создать мощную структуру кода, используя полиморфизм.
Экспериментируйте. Через это, вы сможете овладеть этой мощной концепцией!
Ответ
Как всегда приветствую ваши комментарии и вопросы. И ждём у Виталия на открытом уроке.
Что такое полиморфизм в Java
Что такое полиморфизм
Определение полиморфизма звучит устрашающе 🙂
Так о каких формах идет речь? Давайте сначала приведем примеры и покажем, как на практике проявляется полиморфизм, а потом снова вернемся к его определению.
Как проявляется полиморфизм
Дело в том, что если бы в Java не было принципа полиморфизма, компилятор бы интерпретировал это как ошибку:
Как видите, методы на картинке отличаются значениями, которые они принимают:
Однако, поскольку в Java используется принцип полиморфизма, компилятор не будет воспринимать это как ошибку, потому что такие методы будут считаться разными:
Теперь Вы можете понять, почему часто этот принцип описывают фразой:
Это предполагает, что мы можем заполнить одно название (один интерфейс), по которому мы сможем обращаться к нескольким методам.
Перегрузка методов
Переопределение методов родителя
Когда мы наследуем какой-либо класс, мы наследуем и все его методы. Но если нам хочется изменить какой-либо из методов, который мы наследуем, мы можем всего-навсего переопределить его. Мы не обязаны, например, создавать отдельный метод с похожим названием для наших нужд, а унаследованный метод будет «мертвым грузом» лежать в нашем классе.
Именно то, что мы можем создать в классе-наследнике класс с таким же названием, как и класс, который мы унаследовали от родителя, и называется переопределением.
Пример
Представим, что у нас есть такая структура:
Поэтому, в классах-наследниках мы переопределяем метод voice(), чтобы мы в консоли получали «Мяу», «Гав» и «Муу».
Так что же такое полиморфизм
Давайте снова посмотрим на определение, которое мы давали в начале статьи:
Выглядит понятнее, правда? Мы показали, как можно:
Надеемся, наша статья была Вам полезна. Записаться на наши курсы по Java можно у нас на сайте.
Как полиморфизм реализован внутри JVM
Перевод данной статьи подготовлен специально для студентов курса «Разработчик Java».
В моей предыдущей статье Everything About Method Overloading vs Method Overriding (“Все о перегрузке и переопределении методов”) были рассмотрены правила и различия перегрузки и переопределения методов. В этой статье мы посмотрим, как обрабатывается перегрузка и переопределение методов внутри JVM.
Для примера возьмем классы из предыдущей статьи: родительский Mammal (млекопитающее) и дочерний Human (человек).
На вопрос о полиморфизме мы можем посмотреть с двух сторон: с “логической” и “физической”. Давайте сначала рассмотрим логическую сторону вопроса.
Логическая точка зрения
С логической точки зрения, на этапе компиляции вызываемый метод рассматривается как относящийся к типу ссылки. Но во время выполнения будет вызываться метод объекта, на который указывает ссылка.
Это все довольно просто, пока мы остаемся на концептуальном уровне. Но как же JVM обрабатывает это все внутри? Как JVM вычисляет, какой метод должен быть вызван?
Также мы знаем, что перегруженные методы (overload) не называются полиморфными и резолвятся во время компиляции. Хотя иногда перегрузку методов называют полиморфизмом времени компиляции или ранним/статическим связыванием.
Переопределенные методы (override) резолвятся во время выполнения, так как компилятор не знает, есть ли переопределенные методы в объекте, который присваивается ссылке.
Физическая точка зрения
Команда выше покажет две секции байткода.
1. Пул констант. Содержит почти все, что необходимо для выполнения программы. Например, ссылки на методы ( #Methodref ), классы ( #Class ), литералы строк ( #String ).
2. Байткод программы. Выполняемые инструкции байткода.
Почему перегрузка методов называется статическим связыванием
Таким образом, в случае перегрузки метода, компилятор способен идентифицировать инструкции байткода и адреса методов во время компиляции. Именно поэтому это называют статическим связыванием или полиморфизмом времени компиляции.
Почему переопределение методов называется динамическим связыванием
Для вызова методов anyMammal.speak() и humanMammal.speak() байткод одинаковый, так как с точки зрения компилятора оба метода вызываются для класса Mammal :
Итак, теперь возникает вопрос, если у обоих вызовов одинаковый байткод, как JVM узнает, какой метод вызвать?
Инструкция invokevirtual вызывает метод экземпляра через диспетчеризацию по (виртуальному) типу объекта. Это нормальная диспетчеризация методов в языке программирования Java.
JVM использует инструкцию invokevirtual для вызова в Java методов, эквивалентных виртуальным методам C++. В C++ для переопределения метода в другом классе, метод должен быть объявлен как виртуальный (virtual). Но в Java по умолчанию все методы виртуальные (кроме final и static методов), поэтому в дочернем классе мы можем переопределить любой метод.
Инструкция invokevirtual принимает указатель на метод, который нужно вызвать ( #4 — индекс в пуле констант).
Но ссылка #4 ссылается дальше на другой метод и Class.
Все эти ссылки используются совместно для получения ссылки на метод и класс, в котором находится нужный метод. Это также упоминается в спецификации JVM (прим. переводчика: ссылка на JVM spec 2.7):
Java Virtual Machine не требует какой-либо определённой внутренней структуры объектов.
В некоторых реализациях Java Virtual Machine, выполненных компанией Oracle, ссылка на экземпляр класса представляет собой ссылку на обработчик, который сам по себе состоит из пары ссылок: одна указывает на таблицу методов объекта и указатель на объект Class, представляющий тип объекта, а другая на область данных в куче, содержащую данные объекта.
Это означает, что каждая ссылочная переменная содержит два скрытых указателя:
Из приведенных выше рассуждений можно сделать вывод, что ссылка на объект косвенно содержит ссылку/указатель на таблицу, которая содержит все ссылки на методы этого объекта. Java позаимствовала эту концепцию из C ++. Эта таблица известна под различными именами, такими как таблица виртуальных методов (VMT), таблица виртуальных функций (vftable), виртуальная таблица (vtable), таблица диспетчеризации.
Мы не можем быть уверены в том, как vtable реализован в Java, потому что это зависит от конкретной JVM. Но мы можем ожидать, что стратегия будет примерно такая же, как и в C ++, где vtable — это структура, похожая на массив, которая содержит имена методов и их ссылки. Всякий раз, когда JVM пытается выполнить виртуальный метод, она запрашивает его адрес в vtable.
Для каждого класса существует только одна vtable, это означает, что таблица уникальна и одинакова для всех объектов класса, аналогично объекту Class. Объекты Class подробнее рассмотрены в статьях Why an outer Java class can’t be static и Why Java is Purely Object-Oriented Language Or Why Not.
Инструкция invokevirtual заставляет JVM обрабатывать значение в ссылке на метод # 4 не как адрес, а как имя метода, которое нужно искать в vtable для текущего объекта.
Я надеюсь, теперь стало более понятно то, как JVM использует пул констант и таблицу виртуальных методов для определения того, какой метод вызывать.
Код примера вы можете найти в репозитории Github.
24. Java – Полиморфизм
Полиморфизм – способность объекта принимать множество различных форм. Наиболее распространенное использование полиморфизма в ООП происходит, когда ссылка на родительский класс используется для ссылки на объект дочернего класса. Постараемся разобраться с понятием полиморфизма в Java простыми словами, так сказать для чайников.
Любой объект в Java, который может пройти более одного теста IS-A считается полиморфным. В Java все объекты полиморфны, так как любой объект пройдёт тест IS-A для своего собственного типа и для класса Object.
Важно знать, что получить доступ к объекту можно только через ссылочную переменную. Ссылочная переменная может быть только одного типа. Будучи объявленной, тип ссылочной переменной изменить нельзя.
Ссылочную переменную можно переназначить к другим объектам, которые не объявлены как final. Тип ссылочной переменной определяет методы, которые она может вызвать на объекте.
Ссылочная переменная может обратиться к любому объекту своего объявленного типа или любому подтипу своего объявленного типа. Ссылочную переменную можно объявить как класс или тип интерфейса.
Содержание
Пример 1
Рассмотрим пример наследования полиморфизм в Java.
Теперь класс Deer (Олень) считается полиморфным, так как он имеет множественное наследование. Следующие утверждения верны для примера выше:
Когда мы применяем факты ссылочной переменной к ссылке на объект Deer (Олень), следующие утверждения верны:
Пример 2
Все переменные (d, a, v, o) ссылаются к тому же объекту Deer (Олень).
Виртуальные методы
В этом разделе рассмотрим, как поведение переопределённых методов в Java позволяет воспользоваться преимуществами полиморфизма при оформлении классов.
Мы уже рассмотрели переопредение методов, где дочерний класс может переопределить метод своего «родителя». Переопределённый метод же скрыт в родительском классе и не вызван, пока дочерний класс не использует ключевое слово super во время переопределения метода.
Пример
Теперь предположим, что мы наследуем класс Employee следующим образом:
Теперь, внимательно изучите программу и попытайтесь предугадать её вывод:
После запуска программы будет выдан такой результат:
Итак, мы создали два объекта 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.
Это поведение называется вызовом виртуальных методов, а эти методы называются виртуальными. Переопределённый метод вызывается во время запуска программы вне зависимости от того, какой тип данных был использован в исходном коде во время компиляции.