Слово "полиморфизм" в зависимости от контекста может означать разные вещи. Когда о полиморфизме говорят программисты на императивных языках, они, как правило, подразумевают "полиморфизм подтипов". В то же время программисты на функциональных языках имеют в виду "параметрический полиморфизм". О последнем и поговорим.
В этом уроке появляется код на Java. Не переживайте, если не понимаете его до конца, наша цель — разобраться с концепциями, а не с Java.
В PHP есть встроенная функция array_merge()
, которая объединяет переданные ей массивы:
<?php
array_merge([1], [2, 3, 1]); // [1, 2, 3, 1]
array_merge(['one'], ['two', 'three']); // ['one', 'two', 'three']
array_merge([true], [false, false, true]); // [true, false, false, true]
Эта функция объединяет любые массивы, независимо от типа данных, содержащихся внутри. Попробуем реализовать её самостоятельно.
<?php
// Это немного урезанная версия функции array_merge, она работает только с двумя аргументами
// Функция создает новый массив, затем обходит по очереди переданные массивы и добавляет их значения
// во вновь созданный массив. Затем он возвращается наружу.
function myArrayMerge(array $first, array $last)
{
$result = [];
foreach ($first as $value) {
$result[] = $value;
}
foreach ($last as $value) {
$result[] = $value;
}
return $result;
}
Посмотрите внимательно на этот код. Выполняются ли в нём какие-либо операции над данными внутри массива? Ответ: нет. Эти данные перекладываются из одного массива в другой, но над ними не происходит никаких действий. Наша новая функция myArrayMerge()
, также как и исходная array_merge()
может работать с массивами, содержащими любые типы данных.
Для разработчиков, которые писали только на динамических языках, такое поведение кажется естественным, но в статических языках не всё так просто. Ниже пример определения массивов в Java:
int numbers[] = {3, 1, 2, 5, 4};
String words[] = {"one", "two", "three"};
В глаза бросается необходимость указывать тип. Для первого массива это int
, для второго String
. Нельзя создать массив без указания типа его значений. То же самое касается функций, обрабатывающих массивы:
class Main {
public static void main(String[] args) {
int[] a = {1, 2, 3, 4};
int[] b = {4, 16, 1, 2, 3, 22};
myArrayMerge(a, b);
}
public static int[] myArrayMerge(int[] first, int[] last) {
int[] result = new int[first.length + last.length];
int count = 0;
for (int i = 0; i < first.length; i++) {
result[i] = first[i];
count++;
}
for (int j = 0; j < last.length; j++) {
result[count] = last[j];
count++;
}
return result;
}
}
Обратите внимание на сигнатуру метода myArrayMerge()
: int[] myArrayMerge(int[] first, int[] last)
. В отличие от варианта на PHP, здесь указано, что входными параметрами являются массивы чисел. То есть для массива строк эта функция работать не будет. Не будет она работать и для всех остальных типов данных.
Что это означает на практике? Очень простую и печальную вещь. Нам придётся реализовывать подобную функцию для каждого типа, при том что алгоритм внутри абсолютно идентичен.
Именно тут нам пригодится параметрический полиморфизм. Статическим языкам приходится вводить в язык специальные конструкции, которые позволяют описывать подобные алгоритмы безотносительно типа параметра. В некоторых языках их называют шаблонами (C++) или дженериками (Java, C#):
class Main {
public static void main(String[] args) {
Integer[] a = {1, 2, 3, 4};
Integer[] b = {4, 16, 1, 2, 3, 22};
myArrayMerge(a, b);
}
public static<T> T[] myArrayMerge(T[] first, T[] last) {
T[] result = (T[]) new Object[first.length + last.length];
int count = 0;
for (int i = 0; i < first.length; i++) {
result[i] = first[i];
count++;
}
for (int j = 0; j < last.length;j++) {
result[count] = last[j];
count++;
}
return result;
}
}
В этом коде появляется тип T, что как раз и означает возможность использования с любым типом внутри массива. Теперь функция myArrayMerge
работает подобно аналогу из PHP.
Параметрический полиморфизм даёт возможность писать обобщённые алгоритмы для составных типов, что в некоторых случаях значительно сокращает количество кода. Иногда за это приходится платить сложностью решения, но для большинства типичных операций сложность растёт не сильно. Это видно и по коду выше.
В динамических языках для реализации обобщённых алгоритмов, параметрический полиморфизм не нужен. Любая коллекция может содержать любые типы данных в любой момент времени. Благодаря этому не требуется вводить дополнительных языковых конструкций и изучать новые концепции.
В литературе использование параметрического полиморфизма часто называется обобщённым программированием.
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.