10 июля 2022

☕ Учебник по Java: cтатический и динамический полиморфизм

Веб-разработчик, фрилансер... Пишу об ИТ и смежных технологиях.
В статье разберемся с одной из ключевых концепций объектно-ориентированного программирования — полиморфизмом — и посмотрим, как она реализована в Java.
☕ Учебник по Java: cтатический и динамический полиморфизм

Что такое Полиморфизм?

Слово «полиморфизм» произошло от двух греческих слов: «Poly» означающего «многочисленный» и «Morph» — форма. Если перевести дословно – бесчисленное множество форм. Живые примеры этого явления можно встретить в земной коре, среди животных и рептилий, а также в других областях науки. Но речь сегодня не об этом, а об основной парадигме ООП, применяющейся во многих языках программирования.

Что такое полиморфизм в программировании?

Полиморфизм в ООП подразумевает собой особенность, при которой одно и то же действие выполняется разными способами. Или, проще говоря, такая концепция позволяет выполнять несколько реализаций одной и той же сущности.

Чаще всего она возникает при наличии другой парадигмы ООП — наследования, т. е. когда у нас существует множество классов, связанных друг с другом. Наследование позволяет одному классу приобретать свойства и атрибуты другого, а полиморфизм использует унаследованные свойства для выполнения различных задач.

Примеры из жизни

Чтобы лучше понять принцип парадигмы, попробуем объяснить это с помощью нескольких простых примеров.

Возьмем нас с вами или если совсем обобщенно — человека. Он, в зависимости от различных обстоятельств, может вести себя по-разному. Например, женщина может быть и матерью, и дочерью, и сестрой, и другом одновременно и в разных ситуациях ее действия будут радикально отличаться.

А еще в организме человека есть разные органы и у каждого из них есть своя функция: сердце отвечает за кровоток, легкие за дыхание, мозг за когнитивную деятельность. То есть, у нас есть стандартная функция, работающая по-разному в зависимости от органа тела. Теперь перейдем конкретно к Java.

☕ Подтянуть свои знания по Java вы можете на нашем телеграм-канале «Библиотека 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 для практики можно найти на нашем телеграм-канале «Библиотека задач по Java»

Типы полиморфизма

Два варианта реализации полиморфизма в Java:

  1. Overloading – перегрузка метода
  2. 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();

    }
  }
    

Вывод:

        Животные питаются:
Травоядные едят растения
Всеядные едят растения и мясо
Хищники едят мясо
    

Преимущества и недостатки полиморфизма

  1. Предоставляет возможность повторного использования кода. Реализованные классы можно многократно использовать повторно. Кроме того, это экономит много времени разработчику, ведь при этом появляется возможность поменять что-то в программе, не трогая исходный код.
  2. Одна переменная может использоваться для хранения нескольких значений данных. Значение переменной в дочернем классе, наследуемой от родительского, может быть изменено без изменения родительской переменной.
  3. Легче отлаживать код, когда его не так много.

Помимо преимуществ, у рассматриваемой парадигмы есть еще и несколько недостатков:

  1. Полиморфизм не так просто реализовать на деле.
  2. Такой подход снижает читаемость кода.
  3. Может вызывать серьезные проблемы с производительностью в режиме реального времени.
***

Мы надеемся, что у вас сложилось правильное общее представление о полиморфизме в Java и о том, как его использовать. С более подробной информации по теме можно ознакомиться на официальном сайте Oracle.

Материалы по теме

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ