Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос нашим менторам. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете
JS: Полиморфизм

Параметрический полиморфизм

Слово "полиморфизм" в зависимости от контекста может означать разные вещи. Когда о полиморфизме говорят программисты на императивных языках, они, как правило, подразумевают "полиморфизм подтипов". В то же время программисты на функциональных языках имеют в виду "параметрический полиморфизм". О последнем и поговорим.

В этом уроке появляется код на Java. Не переживайте, если не понимаете его до конца. Наша цель — разобраться с концепциями, а не с Java.

В библиотеке lodash есть функция _.concat(), которая объединяет переданные ей массивы:

_.concat([1], [2, 3, 1]); // [1, 2, 3, 1]
_.concat(['one'], ['two', 'three']); // ['one', 'two', 'three']
_.concat([true], [false, false, true]); // [true, false, false, true]

Эта функция объединяет любые массивы, независимо от типа данных, содержащихся внутри. Попробуем реализовать её самостоятельно.

// Это немного урезанная версия функции concat, она работает только с двумя аргументами,
// каждый из которых – массив
// Функция создаёт новый массив, затем обходит по очереди переданные массивы
// и добавляет их значения во вновь созданный массив. Затем он возвращается наружу.
const concat = (coll1, coll2) => {
    const result = [];
    coll1.forEach((value) => result.push(value));
    coll2.forEach((value) => result.push(value));
    return result;
};

Посмотрите внимательно на этот код. Выполняются ли в нём какие-либо операции над данными внутри массива? Правильный ответ — нет. Эти данные перекладываются из одного массива в другой, но над ними не происходит никаких действий. Наша новая функция concat(), также как и исходная _.concat(), может работать с массивами, содержащими любые типы данных.

Для разработчиков, которые писали только на динамических языках, такое поведение кажется естественным, но в статических языках не всё так просто. Ниже пример определения массивов в Java:

int numbers[] = {3, 1, 2, 5, 4}; 
String words[] = {"one", "two", "three"}; 

В глаза бросается необходимость указывать тип. Для первого массива это int, для второго String. Нельзя создать массив без указания типа его значений. То же самое касается функций, обрабатывающих массивы:

class Main {
    public static void main(String[] args) {
        // Объявление массива a
        int[] a = {1, 2, 3, 4};
        // Объявление массива b
        int[] b = {4, 16, 1, 2, 3, 22};

        // Сливаем массивы
        concat(a, b);
    }

    // На вход могут приходить массивы содержащие только int
    public static int[] concat(int[] arr1, int[] arr2) {
        // Создаем результирующий массив длина которого, равна сумме длин исходных массивов
        int[] result = new int[arr1.length + arr2.length];

        // Переносим в result все значения из первого массива
        for (int i = 0; i < arr1.length; i++) { 
           result[i] = arr1[i];
        } 

        // Переносим в result все значения из второго массива
        for (int j = 0; j < arr2.length; j++) { 
           result[arr1.length + j] = arr2[j];
        }

        return result;
    }
}

Обратите внимание на сигнатуру метода concat(): int[] concat(int[] arr1, int[] arr2). В отличие от варианта на JavaScript здесь указано, что входными параметрами являются массивы чисел. То есть для массива строк эта функция работать не будет. Не будет она работать и для всех остальных типов данных.

Что это означает на практике? Очень простую и печальную вещь. Нам придётся реализовывать подобную функцию для каждого типа при том, что алгоритм внутри абсолютно идентичен.

Именно тут нам пригодится параметрический полиморфизм. Статическим языкам приходится вводить в язык специальные конструкции, которые позволяют описывать подобные алгоритмы безотносительно типа параметра. В некоторых языках их называют шаблонами (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};
    concat(a, b);
  }

  public static<T> T[] concat(T[] arr1, T[] arr2) {
      T[] result = (T[]) new Object[arr1.length + arr2.length];

      for (int i = 0; i < arr1.length; i++) { 
         result[i] = arr1[i];
      } 

      for (int j = 0; j < arr2.length; j++) {
         result[arr1.length + j] = arr2[j];
      }

      return result;
  }
}

В этом коде появляется тип T, что как раз и означает возможность использования с любым типом внутри массива. Теперь метод concat() работает подобно аналогу из JavaScript.

Параметрический полиморфизм даёт возможность писать обобщённые алгоритмы для составных типов, что в некоторых случаях значительно сокращает количество кода. Иногда за это приходится платить сложностью решения, но для большинства типичных операций сложность растёт не сильно. Это видно и по коду выше.

В динамических языках для реализации обобщённых алгоритмов параметрический полиморфизм не нужен. Любая коллекция может содержать любые типы данных в любой момент времени. Благодаря этому не требуется вводить дополнительные языковые конструкции и изучать новые концепции.

В литературе использование параметрического полиморфизма часто называется обобщённым программированием.


<span class="translation_missing" title="translation missing: ru.web.courses.lessons.mentors.mentor_avatars">Mentor Avatars</span>

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят менторы из команды Хекслета или другие студенты.

Для полного доступа к курсу нужна профессиональная подписка

Профессиональная подписка откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, даст возможность обращаться за помощью к менторам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
115
курсов
892
упражнения
2241
час теории
3196
тестов

Зарегистрироваться

или войти в аккаунт

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг».

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг».