Chameleon on the tree branch in Uganda, Africa
Ви знаєте такий пристрій, як пульт дистанційного керування? Його використовують для керування домашньою технікою і одна й та сама кнопка може виконувати декілька різних функцій. Наприклад, кнопка живлення вмикає або вимикає телевізор.
У програмуванні поліморфізм працює так само. Об’єкти можуть набувати різних форм залежно від контексту, в якому вони використовуються.
Це робить поліморфізм потужним інструментом створення гнучкого коду. Він широко використовується в багатьох мовах програмування та середовищах, спрощує обслуговування та оновлення коду, а також робить програми надійнішими і менш схильними до помилок.
Поліморфізм — це концепція програмування, яка дозволяє обробляти об’єкти різних типів так, ніби вони належать до одного типу, і по-різному реагувати на одні й ті самі методи чи функції.
Інакше кажучи, поліморфізм дозволяє різним об’єктам спільно використовувати той самий інтерфейс, але поводитися по-різному залежно від своїх цілей виконання.
Наприклад, у вас є функція, яка приймає на вхід об’єкт Animal
. Вона може працювати з будь-яким типом тварин: кішкою, собакою, хом’ячком. У кожного типу тварин буде своя реалізація функції, але самій функції не потрібно знати, з яким конкретним типом тварин вона працює. Саме ця особливість поліморфізму дозволяє з його допомогою створювати гнучкий та повторно використовуваний код.
Поліморфізм виник, коли сформувалася концепція об’єктно-орієнтованого програмування (ООП), що зробило його невід’ємною частиною ООП, і однією з ключових складових мов програмування загалом.
Ідею поліморфізму вперше представив у середині 1960-х Крістофер Стрейчі у своїй статті «Фундаментальні концепції мов програмування». Але лише наприкінці 1970-х — початку 1980-х років ця концепція почала впроваджуватися у мови програмування.
Однією з перших мов програмування, які використовують поліморфізм, стала Simula (розроблена наприкінці 1960-х – початку 1970-х років).
Simula розробляли для симуляції та моделювання. Вона представила концепцію об’єктів, класів та успадкування, завдяки чому стала першою мовою, яка використовує термін «віртуальний» для опису поліморфізму.
Smalltalk, яку створили в 1970-ті роки, теж один із ранніх прикладів мови програмування, де було введено концепцію поліморфізму.
Її зробили для освітніх цілей, вона був однією з перших повністю ОО-мов програмування та представила концепцію передачі повідомлень, яка дозволяла об’єктам спілкуватися один з одним.
Важливу роль у розвитку поліморфізму зіграла C++. Її розробили в 1980-х роках як розширення мови програмування C і включили до неї концепцію перевантаження функцій, яка дозволяла кільком функціям мати одне й те саме ім’я, але різні параметри.
C++ також представила концепцію шаблонів, яка дозволила використати універсальне програмування та ще більше розширила можливості поліморфізму.
Мова Java, яка з’явилася в середині 1990-х років, також активно використовує поліморфізм. Основним завданням розробників Java було створення універсальної мови, яка могла б працювати на будь-яких пристроях. Використання концепції поліморфізму стало ключовим елементом досягнення цієї мети.
Зокрема, Java представила концепцію інтерфейсів, яка дозволяла класам реалізовувати кілька інтерфейсів та успадковувати поведінку з кількох джерел.
Зараз поліморфізм — одна з ключових концепцій ОО-мов програмування і широко застосовується при розробці складних програмних систем.
Завдяки використанню поліморфізму розробники можуть створювати більш гнучкі та універсальні програми, які простіше адаптуються до різних ситуацій та умов.
Хоча поліморфізм — дуже важливий інструмент у програмуванні, як й будь-яка інша концепція, він має і переваги, і недоліки.
Почнемо з переваг:
Недоліки поліморфізму:
У цілому переваги поліморфізму зазвичай переважують недоліки, але важливо враховувати особливості саме вашого ПЗ, перш ніж застосовувати концепцію.
Якщо проаналізувати недоліки поліморфізму, можна дійти висновку, що проблеми виникають, коли його використовують занадто часто і занадто багато. Тому потрібно бути вдвічі обережнішими з концепцією, якщо у вашому коді складна ієрархія, а програмне забезпечення, яке ви розробляєте, важке і об’ємне.
Класифікувати поліморфізм стали відносно недавно. Якщо сама концепція з’явилася ще у 60-х, то класифікацію запропонували лише у середині 1990-х. До цього поліморфізм описувався узагальнено.
Лука Карделлі, італійський вчений у галузі комп’ютерних наук та дослідник мов програмування, виділив такі три види поліморфізму:
Класифікація Карделлі — одна із найпопулярніших і вплинула на розвиток багатьох мов програмування, включаючи Java та C++. Але вона не єдина: є й альтернативні підходи до класифікації поліморфізму. Наприклад, є ще класифікація за Бертраном Мейєром. Вона ґрунтується на концепції поліморфізму підтипів, яку він розробив як частину своєї роботи над мовою програмування Eiffel.
Інший підхід до класифікації ділить поліморфізм на два основні типи:
Поліморфізм часу компіляції, також відомий як статичний поліморфізм, виникає, коли компілятор знаходиться в стадії визначення, яку функцію або метод викликати під час компіляції, ґрунтуючись на переданих параметрах.
Зазвичай це досягається шляхом перевантаження методу. Він визначає методи з одним ім’ям, але різними параметрами, щоб забезпечити можливість роботи з різними типами значень.
Приклад:
Припустимо, у нас є такий код на Java:
public class Calculator { public int add(int a, int b) { return a + b; } public double add(double a, double b) { return a + b; } public int add(int a, int b, int c) { return a + b + c; } }
Клас Calculator
визначає три методи add
. Вони мають різні параметри та значення, які повертаються:
Залежно від того, скільки аргументів та яких типів буде подано на вхід, компілятор Java визначить, який із реалізації методу add
використати у цьому конкретному випадку.
Іншими словами, якщо скласти потрібно буде два цілих числа, метод поверне суму цілим числом, а якщо два числа подвійної точності — сума повернеться також числом з подвійною точністю.
Інша назва поліморфізму часу виконання — динамічний поліморфізм. Він виникає, коли метод або функція, яка викликається, визначається під час виконання на основі конкретного об’єкта.
Через це поліморфізм часу компіляції ефективніший за поліморфізм часу виконання — адже дозвіл методу відбувається на етапі компіляції. Але водночас динамічний поліморфізм гнучкіше, оскільки дозволяє об’єктам виявляти різне поведінку залежно від своїх конкретних типів.
Приклад:
У Java приклад поліморфізму часу виконання — можливість перевизначення методів. Підклас надає свою реалізацію методу, який вже визначено у його суперкласі:
class Animal { public void move() { System.out.println("Animals can move"); } } class Dog extends Animal { public void move() { System.out.println("Dogs can walk and run"); } } public class TestDog { public static void main(String args[]) { Animal animal = new Animal(); // Animal reference and object Animal dog = new Dog(); // Animal reference but Dog object animal.move(); // runs the method in Animal class dog.move(); // runs the method in Dog class } }
У цьому прикладі клас Animal
має метод move
, який виводить повідомлення у консоль. Клас Dog
розширює клас Animal
та перевизначає метод move
:
Animal
та викликаємо його метод move
, викликається версія, визначена у класі Animal
, і в консоль виводиться повідомлення «Animals can move»;Dog
і викликаємо його метод move
, викликається версія методу, визначена в класі Dog
, і в консоль виводиться повідомлення «Dogs can walk and run».Ad-hoc поліморфізм
Виникає, коли функцію або метод визначено для конкретного набору типів.
Візьмемо код на С++:
#include <iostream> int add(int a, int b) { std::cout << "Called the int version" << std::endl; return a + b; } double add(double a, double b) { std::cout << "Called the double version" << std::endl; return a + b; } int main() { std::cout << add(2, 3) << std::endl; // calls the int version std::cout << add(2.0, 3.0) << std::endl; // calls the double version return 0; }
Ми маємо дві функції add
з різними типами параметрів: одна приймає два цілих числа, а інша — два числа подвійної точності. Реалізація функції визначається під час компіляції на основі типів аргументів, переданих у функцію.
Цей вид трохи схожий на поліморфізм часу компіляції, але в цьому випадку відмінності методів виявляються лише у типі, а не у кількості аргументів.
Параметричний поліморфізм
Виникає, коли функція чи метод визначаються загальним способом і є можливість його використання з багатьма іншими типами.
Візьмемо такий код на Java:
public class Pair<T, U> { private T first; private U second; public Pair(T first, U second) { this.first = first; this.second = second; } public T getFirst() { return first; } public void setFirst(T first) { this.first = first; } public U getSecond() { return second; } public void setSecond(U second) { this.second = second; } public void print() { System.out.println("(" + first.toString() + ", " + second.toString() + ")"); } } public class TestPair { public static void main(String[] args) { Pair<String, Integer> pair1 = new Pair<>("Hello", 123); Pair<Double, Boolean> pair2 = new Pair<>(3.14, true); pair1.print(); // prints "(Hello, 123)" pair2.print(); // prints "(3.14, true)" } }
Тут у нас є клас Pair
, який приймає два параметри універсального типу T
та U
. Об’єкти Pair
створюються з різними типами для T
та U
, і код може бути використаний для будь-якої можливої комбінації типів.
Поліморфізм примусу
Виникає, коли компілятор автоматично перетворює один тип на інший для відповідності сигнатурі функції або методу.
Якщо ви додасте рядок і число в JavaScript, мова автоматично перетворює число в рядок і з’єднає два значення:
let stringVar = "5"; let numberVar = 10; let result = stringVar + numberVar; // result will be "510"
Оператор +
використовується для об’єднання stringVar
та numberVar
. Оскільки stringVar
— це рядок, а numberVar
— це число, перед об’єднанням двох значень компілятор автоматично перетворює numberVar
на рядок.
Щоб створювати гнучкіші і складніші програмні системи, деякі типи поліморфізму можна комбінувати. Це стосується таких типів:
Наприклад, функція, яка використовує параметричний поліморфізм для роботи з будь-яким типом даних, може також використовувати поліморфізм ad-hoc.
Приклад:
Код на Python, що демонструє поєднання ad-hoc поліморфізму та параметричного поліморфізму:
def add(x, y): return x + y def add_strings(x: str, y: str): return x + '' + y print(add(2, 3)) # Output: 5 print(add('Hello', 'World')) # Output: Hello World print(add_strings('Hello', 'World')) # Output: Hello World
Ми визначили дві функції з ім’ям add
:
add
— це універсальна функція, яка може приймати дані будь-якого типу, що підтримує оператор +
;add_strings
— це спеціалізована функція, яка приймає лише два вхідні рядки та об’єднує їх за допомогою символу пропуску.Функція add
демонструє ad-hoc поліморфізм, тому що вона може обробляти різні типи даних та забезпечувати бажану операцію залежно від типу її аргументів.
Функція add_strings
демонструє поєднання ad-hoc поліморфізму та параметричного поліморфізму, тому що це спеціалізована функція, яка працює тільки з рядками, за умови, що тип вхідних даних явно вказаний.
Один із прикладів прояву поліморфізму — це зміна реалізації методів при наслідуванні.
Припустимо, у нас є такий код на Java:
class Animal { void makeSound() { System.out.println("The animal makes a sound"); } } class Dog extends Animal { void makeSound() { System.out.println("The dog barks"); } } class Cat extends Animal { void makeSound() { System.out.println("The cat meows"); } }
У нас є базовий клас Animal
та два похідні класи Dog
та Cat
. Клас Animal
має метод makeSound()
, який друкує стандартне повідомлення про звук. Класи Dog
та Cat
перевизначають цей метод, щоб вивести, який звук відтворюють ці тварини.
Припустимо, що ми створюємо об’єкти кожного з цих класів і викликаємо метод makeSound()
:
Animal animal = new Animal(); Dog dog = new Dog(); Cat cat = new Cat(); animal.makeSound(); // The animal makes a sound dog.makeSound(); // The dog barks cat.makeSound(); // The cat meows
Ми бачимо, що хоча всі три об’єкти були створені з різних класів, вони реагують на метод makeSound()
. Це якраз властивість поліморфізму — здатність об’єктів набувати різних форм залежно від їхнього контексту.
Поліморфізм — це ключова концепція об’єктно-орієнтованого програмування, яка дозволяє об’єктам набувати різних форм або поводитися по-різному залежно від контексту.
Цей інструмент дозволяє програмістам писати більш гнучкий та зручний у супроводі код. Розробляючи наш код як поліморфний, ми можемо створювати більш досконалі програмні системи, а також робити менше помилок.
Наприклад, ми можемо написати спільну функцію, яка працює з різними типами об’єктів, а не писати окрему функцію для кожного типу. Це може значно заощадити час і зменшити кількість багів, тому що у нас не буде дублювання коду.
У той же час є ризик погіршити продуктивність ПЗ, якщо поліморфізм використовувати занадто часто. Тому завжди ретельно зважуйте ризики, перш ніж застосовувати концепцію.
Резиденти Дія.City сплатили до бюджету понад 8 млрд грн податків в І кварталі 2025 року.…
У Китаї закликають офісних працівників не працювати надто багато — держава сподівається, що вільний час…
Експерти звертають увагу на тривожну тенденцію: люди все частіше використовують ChatGPT, щоб визначити місцезнаходження, зображене…
Компанія JetBrains випустила нову версію мультимовного середовища розробки IntelliJ IDEA 2025.1. Оновлена IDE отримала численні…
Платформа обміну миттєвими повідомленнями Discord впроваджує функцію перевірки віку за допомогою сканування обличчя. Зараз вона…
Wikipedia намагається захистити себе від тисяч різноманітних ботів-скрейперів, які сканують дані цієї платформи для навчання…