Что такое Полиморфизм?
Слово «полиморфизм» произошло от двух греческих слов: «Poly» означающего «многочисленный» и «Morph» — форма. Если перевести дословно – бесчисленное множество форм. Живые примеры этого явления можно встретить в земной коре, среди животных и рептилий, а также в других областях науки. Но речь сегодня не об этом, а об основной парадигме ООП, применяющейся во многих языках программирования.
Что такое полиморфизм в программировании?
Полиморфизм в ООП подразумевает собой особенность, при которой одно и то же действие выполняется разными способами. Или, проще говоря, такая концепция позволяет выполнять несколько реализаций одной и той же сущности.
Чаще всего она возникает при наличии другой парадигмы ООП — наследования, т. е. когда у нас существует множество классов, связанных друг с другом. Наследование позволяет одному классу приобретать свойства и атрибуты другого, а полиморфизм использует унаследованные свойства для выполнения различных задач.
Примеры из жизни
Чтобы лучше понять принцип парадигмы, попробуем объяснить это с помощью нескольких простых примеров.
Возьмем нас с вами или если совсем обобщенно — человека. Он, в зависимости от различных обстоятельств, может вести себя по-разному. Например, женщина может быть и матерью, и дочерью, и сестрой, и другом одновременно и в разных ситуациях ее действия будут радикально отличаться.
А еще в организме человека есть разные органы и у каждого из них есть своя функция: сердце отвечает за кровоток, легкие за дыхание, мозг за когнитивную деятельность. То есть, у нас есть стандартная функция, работающая по-разному в зависимости от органа тела. Теперь перейдем конкретно к Java.
Полиморфизм в Java
Что мы должны понимать про принцип действия рассматриваемой концепции? Ну во-первых, все то, что содержит или обрабатывает значения различных типов, когда идет компиляция или выполняется программа — является полиморфным, например:
- любая переменная, которой присваивается значение другого типа;
- любой объект, обладающий свойствами, изменяющими тип данных присвоенного ему значения;
- любая функция, принимающая аргументы различных типов.
Рассмотрим небольшой пример по реализации полиморфизма на Java:
У суперкласса под названием Forms
есть метод shapearea()
и классы-потомки Triang
и Circ
. Каждый из них имеет свой способ подсчета площади. С помощью рассматриваемой парадигмы потомки пользуются методом родителя shapearea()
, чтобы найти формулу для расчета своей площади.
class Forms {
public void shapearea() {
System.out.println("Формулы:");
}
}
class Triang extends Forms{
public void shapearea() {
System.out.println("Sтреугольника: ½ * основания * высоту");
}
}
class Circ extends Forms{
public void shapearea() {
System.out.println("Sкруга: 3,14 * радиус * радиус");
}
}
class Main {
public static void main(String[] args) {
Forms xForms = new Forms); // создание объекта Forms
Forms xTriang= new Triang(); // создание объекта Triang
Forms xCirc = new Circ(); // создание объекта Circ
xForms.shapearea();
xTriang.shapearea();
xForms.shapearea();
xCirc.shapearea();
}
}
Вывод:
Формула: площадь треугольника: ½ * основания * высоту
Формула: площадь круга: 3,14 * радиус * радиус
Типы полиморфизма
Два варианта реализации полиморфизма в Java:
- Overloading – перегрузка метода
- Overriding – переопределение метода
При перегрузке метода мы в одном классе создаем нескольких методов с одинаковым названием, но разным функционалом, например:
class Forms{
public void shapearea()) {
System.out.println("Площади фигур:");
}
public void shapearea(int r) {
System.out.println("Sкруга = "+3.14*r*r);
}
public void shapearea(double b, double h) {
System.out.println("Sтреугольника ="+0.5*b*h);
}
public void shapearea(int l, int b) {
System.out.println("Sпрямоугольника ="+l*b);
}
}
class Main {
public static void main(String[] args) {
Forms xForms = new Forms();
xForms.shapearea();
xForms.shapearea(3);
xForms.shapearea(2.5, 2.0);
xForms.shapearea(4,7);
}
}
Вывод:
Площади фигур:
Sкруга = 28.26
Sтреугольника = 5
Sпрямоугольника = 28
При переопределении название метода дочернего класса мы ставим такое же, как и уже объявленного метода родительского класса, например:
class Vehicles{
// определение метода
void drive(){
System.out.println("Управлять транспортным средством");}
}
//создаем дочерний класс
class Cars extends Vehicles{
//определяем одноименный метод
void drive(){
System.out.println("Управлять автомобилем");
}
public static void main(String args[]){
Cars model= new Cars(); //создание нового объекта
model.drive(); //вызов переопределенного метода
}
}
Выведет:
Управлять автомобилем
Статический и динамический полиморфизм
При статическом варианте развития событий метод можно вызывать при компиляции кода, происходит это при помощи рассмотренной нами ранее концепции — перегрузки методов. Говоря проще, у них будет одинаковое название, но разная функциональная начинка (тип возвращаемых данных, свойств и т. д.). В таком случае Java позволяет пользователю свободно определять функциям идентичные названия, если они будут отличны по типу и параметрам, например:
public class Add {
void sum(int x, int y) {
int z = x + y;
System.out.println(“Сумма двух чисел: ”+z);
}
void sum(int x, int y, int i) {
int z = x + y + i;
System.out.println(“Сумма трех чисел: ”+z);
}
public static void main(String[] args) {
Add myAdd = new Add();
myAdd.sum(5, 11);
myAdd.sum(1, 8, 11);
}
}
Вывод программы:
Сумма двух чисел: 16
Сумма трех чисел: 20
При динамическом полиморфизме вызвать переопределеный метод можно при выполнении программы. Скопировать метод родительского класса и обозначить его в дочернем можно с помощью ссылочной переменной родительского класса, при этом вызванный метод будет определяться по объекту, на который она ссылается. Такую операцию еще называют Upcasting, например:
Создадим один родительский класс Звери
и три подкласса: травоядные
, плотоядные
и всеядные
. В этом случае классы-потомки расширили родителя и переопределили его метод eat()
.
class Beast {
void eat() {
System.out.println("Животные питаются:");
}
}
class herbivorous extends Beast {
void eat() {
System.out.println("Травоядные едят растения");
}
}
class omnivorous extends Beast {
void eat() {
System.out.println("Всеядные едят растения и мясо");
}
}
class carnivorous extends Beast {
void eat() {
System.out.println("Хищники едят только мясо");
}
}
class main {
public static void main(String args[]) {
Beast X = new Beast();
Beast herb = new herbivorous();
Beast omni = new omnivorous();
Beast carn = new carnivorous();
X.eat();
herb.eat();
omni.eat();
carn.eat();
}
}
Вывод:
Животные питаются:
Травоядные едят растения
Всеядные едят растения и мясо
Хищники едят мясо
Преимущества и недостатки полиморфизма
- Предоставляет возможность повторного использования кода. Реализованные классы можно многократно использовать повторно. Кроме того, это экономит много времени разработчику, ведь при этом появляется возможность поменять что-то в программе, не трогая исходный код.
- Одна переменная может использоваться для хранения нескольких значений данных. Значение переменной в дочернем классе, наследуемой от родительского, может быть изменено без изменения родительской переменной.
- Легче отлаживать код, когда его не так много.
Помимо преимуществ, у рассматриваемой парадигмы есть еще и несколько недостатков:
- Полиморфизм не так просто реализовать на деле.
- Такой подход снижает читаемость кода.
- Может вызывать серьезные проблемы с производительностью в режиме реального времени.
Мы надеемся, что у вас сложилось правильное общее представление о полиморфизме в Java и о том, как его использовать. С более подробной информации по теме можно ознакомиться на официальном сайте Oracle.
Комментарии
public void shapearea(double b, double h) { System.out.println("треугольника ="+0.5bh); Как в этом примере получилось, 5, без еще одной 2?
0.5 x 2.5 x 2 = 2.5
В примере результат неправильный.
Все объясняют, что такое полиморфизм и переопределение методов. Но никто не объясняет зачем оно нужно? Смысл? В примерах, которые вы написали, переопределение метода легко можно было заменить просто написанием нового метода. И кода было бы меньше в итоге. Так зачем нужно это переопределение методов?
В классе Beast в строке System.out.println("Животные питаются:”); неправильные кавычка закрывающая.
Спасибо, исправили.
А разве не 3 типа полиморфизма? 1)AdHoc(перегрузка) 2)Параметрический(дженерики) 3)Полиморфизм подтипов(основанный на наследовании и переопределении или интерфейсах)